diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml index b08635993b..3caac0256d 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yml +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -60,6 +60,15 @@ body: validations: required: true + - type: input + id: android_version + attributes: + label: Android version + description: Go to your Settings app and find your _Android_ version. NOT the HyperOS, MIUI, OxygenOS, OneUI version. + placeholder: Android 15 + validations: + required: true + - type: input id: device attributes: diff --git a/.github/workflows/pull-request.yml b/.github/workflows/pull-request.yml index a2027703b1..1acb88303f 100644 --- a/.github/workflows/pull-request.yml +++ b/.github/workflows/pull-request.yml @@ -17,9 +17,6 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Unit tests run: bash ./gradlew testDebugUnitTest @@ -46,12 +43,48 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Ktlint check run: ./gradlew ktlintCheck + rust: + name: Rust code style and tests + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + evdev/src/main/rust/evdev_manager/target/ + key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-rust- + + - name: Check Rust formatting + working-directory: evdev/src/main/rust/evdev_manager + run: cargo fmt --check + + - name: Run Rust tests + working-directory: evdev/src/main/rust/evdev_manager + run: cargo test --package evdev_manager_core + apk: name: Build APK runs-on: ubuntu-latest @@ -77,6 +110,18 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v2 + with: + ndk-version: "27.2.12479018" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android - name: set up Ruby for fastlane uses: ruby/setup-ruby@v1 diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 18188155de..5fc9e345b2 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -23,9 +23,6 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Unit tests run: bash ./gradlew testDebugUnitTest @@ -52,14 +49,50 @@ jobs: java-version: 17 cache: 'gradle' - - name: Setup Android SDK - uses: android-actions/setup-android@v2 - - name: Ktlint check run: ./gradlew ktlintCheck + rust: + name: Rust code style and tests + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + with: + components: rustfmt, clippy + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android + + - uses: actions/cache@v3 + with: + path: | + ~/.cargo/bin/ + ~/.cargo/registry/index/ + ~/.cargo/registry/cache/ + ~/.cargo/git/db/ + evdev/src/main/rust/evdev_manager/target/ + key: ${{ runner.os }}-rust-${{ hashFiles('**/Cargo.toml', '**/Cargo.lock') }} + restore-keys: | + ${{ runner.os }}-rust- + + - name: Check Rust formatting + working-directory: evdev/src/main/rust/evdev_manager + run: cargo fmt --check + + - name: Run Rust tests + working-directory: evdev/src/main/rust/evdev_manager + run: cargo test --package evdev_manager_core + apk: - name: Generate and upload APK to Discord + name: Build APK runs-on: ubuntu-latest steps: - name: Checkout repository @@ -89,6 +122,18 @@ jobs: - name: Setup Android SDK uses: android-actions/setup-android@v2 + with: + ndk-version: "27.2.12479018" + + - name: Setup Rust + uses: dtolnay/rust-toolchain@stable + + - name: Install Android Rust targets + run: | + rustup target add armv7-linux-androideabi + rustup target add aarch64-linux-android + rustup target add i686-linux-android + rustup target add x86_64-linux-android - name: set up Ruby for fastlane uses: ruby/setup-ruby@v1 @@ -122,21 +167,6 @@ jobs: name: ${{ env.APK_NAME }} path: app/build/outputs/apk/ci/${{ env.APK_NAME }}.apk - - name: Upload to Discord - uses: sinshutu/upload-to-discord@v2.0.0 - if: github.event.repository.fork == false - env: - DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }} - with: - args: app/build/outputs/apk/ci/${{ env.APK_NAME }}.apk - - - name: Report build status to Discord - uses: sarisia/actions-status-discord@v1 - if: github.event.repository.fork == false && failure() - with: - title: "Build apk" - webhook: ${{ secrets.DISCORD_BUILD_STATUS_WEBHOOK }} - synchronize-with-crowdin: runs-on: ubuntu-latest diff --git a/.gitignore b/.gitignore index e0116b2eb1..9d7b825226 100644 --- a/.gitignore +++ b/.gitignore @@ -94,3 +94,7 @@ app/.env /.idea/AndroidProjectSystem.xml /.idea/runConfigurations.xml /.idea/studiobot.xml + +evdev/build +evdev/.cxx +evdev/src/main/rust/*/target \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e66269720..be7db7909c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,19 +1,49 @@ +## [4.0.0 Beta 4](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.04) + +#### 25 December 2025 + +Merry Christmas from the Key Mapper team! 🎄 + +Renamed PRO mode to Expert mode because it sounded like a paid premium feature even though it is free. + +## Added + +- #1915 ask user to remove "adb shell" from Shell command. +- #1904 inform the user how to enable the accessibility service with PRO mode or ADB. +- #1911 constraint for physical device orientation that ignores auto rotate setting. +- #1918 improve how key event actions are performed with system bridge. +- #1905 system bridge log is now visible in Key Mapper log. +- #1941 show loading indicator when starting system bridge. + +## Bug fixes + +- #1913 actually save the option to detect with scan code +- #1931 fix Close and Remove From Recents action on some Android 13 revisions +- #1926 PRO mode triggers for external devices work when the device reconnects. +- #1918 PRO mode key maps can input key codes that aren't originally supported by the trigger + device. +- #1934 hold down option for Tap Screen action is added back. +- Log less verbose. + ## [4.0.0 Beta 3](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.03) #### 25 November 2025 ## Added + - #1871 action to modify any system settings. - #1221 action to show a custom notification. - #1491 action to toggle/enable/disable hotspot. - #1414 constraint for when the keyboard is showing. - #1900 log to logcat if extra logging is enabled. - #1902 add toggle next to record trigger button to use PRO mode. +- #1909 categorise constraints similar to actions. ## Bug fixes - #1901 prompt user to set default USB configuration to 'No data transfer' after starting pro mode. -- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug they introduced. +- #1898 do not launch directly into the Wireless Debugging activity on Xiaomi devices due to a bug + they introduced. ## [4.0.0 Beta 2](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.02) @@ -21,12 +51,15 @@ ## Added -- #1890 add button to save log to file and share it. The clipboard button now cuts off older entries and keeps newest ones. +- #1890 add button to save log to file and share it. The clipboard button now cuts off older entries + and keeps newest ones. ## Fixed -- Only autostart PRO mode with Shizuku if Shizuku permission is granted. Otherwise fallback to method with Wireless Debugging and WRITE_SECURE_SETTINGS permission. -- Starting system bridge for the first time would be janky because granting READ_LOGS kills the app process. Only grant for READ_LOGS when sharing logcat from settings. +- Only autostart PRO mode with Shizuku if Shizuku permission is granted. Otherwise fallback to + method with Wireless Debugging and WRITE_SECURE_SETTINGS permission. +- Starting system bridge for the first time would be janky because granting READ_LOGS kills the app + process. Only grant for READ_LOGS when sharing logcat from settings. - #1886 mobile data actions work in PRO mode. ## [4.0.0 Beta 1](https://github.com/sds100/KeyMapper/releases/tag/v4.0.0-beta.01) diff --git a/README.md b/README.md index 854cd7cd51..473668047b 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ Get it on Google Play Get it on F-Droid
-Buy Me a Coffee at ko-fi.com +Buy Me a Coffee at ko-fi.com

diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 2b0504c7a9..7d234f10e3 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -152,6 +152,7 @@ dependencies { implementation(project(":data")) implementation(project(":sysbridge")) implementation(project(":system")) + implementation(project(":evdev")) compileOnly(project(":systemstubs")) coreLibraryDesugaring(libs.desugar.jdk.libs) diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro index 3ba9020698..1e905036f2 100644 --- a/app/proguard-rules.pro +++ b/app/proguard-rules.pro @@ -116,7 +116,7 @@ # Keep all AIDL interface classes and their methods -keep class io.github.sds100.keymapper.sysbridge.ISystemBridge** { *; } --keep class io.github.sds100.keymapper.sysbridge.IEvdevCallback** { *; } +-keep class io.github.sds100.keymapper.evdev.IEvdevCallback** { *; } -keep class io.github.sds100.keymapper.sysbridge.IShizukuStarterService** { *; } -keepclassmembers class io.github.sds100.keymapper.sysbridge.shizuku.ShizukuStarterService { @@ -138,7 +138,10 @@ -keep class io.github.sds100.keymapper.sysbridge.** extends android.content.ContentProvider { *; } # Keep parcelable classes used in AIDL --keep class io.github.sds100.keymapper.common.models.EvdevDeviceHandle { *; } +-keep class io.github.sds100.keymapper.common.models.GrabbedDeviceHandle { *; } +-keep class io.github.sds100.keymapper.common.models.EvdevDeviceInfo { *; } +-keep class io.github.sds100.keymapper.common.models.GrabTargetKeyCode { *; } +-keep class io.github.sds100.keymapper.common.models.ShellResult { *; } # Keep all rikka.hidden classes and interfaces as they contain AIDL files -keep class rikka.hidden.** { *; } @@ -241,4 +244,4 @@ -dontwarn android.view.DisplayInfo -dontwarn android.view.IWindowManager** -dontwarn com.android.internal.app.** --dontwarn com.android.internal.policy.** \ No newline at end of file +-dontwarn com.android.internal.policy.** diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c4e87ba265..d74d55e0eb 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -13,7 +13,7 @@ - + viewModelScope.launch { - navigateToAdvancedTriggers("purchase_assistant_trigger") - } + TriggerSetupShortcut.ASSISTANT, + TriggerSetupShortcut.FLOATING_BUTTON_CUSTOM, + TriggerSetupShortcut.FLOATING_BUTTON_LOCK_SCREEN, + -> viewModelScope.launch { + navigateToAdvancedTriggers("purchase_assistant_trigger") + } - else -> super.showTriggerSetup(shortcut, forceProMode) + else -> super.showTriggerSetup(shortcut, forceExpertMode) } } } diff --git a/app/version.properties b/app/version.properties index 003ec7e3c3..6a17534a00 100644 --- a/app/version.properties +++ b/app/version.properties @@ -1,3 +1,2 @@ -VERSION_NAME=4.0.0-beta.3 -VERSION_CODE=194 -VERSION_NUM=01 \ No newline at end of file +VERSION_NAME=4.0.0-beta.04 +VERSION_CODE=217 diff --git a/base/src/main/assets/whats-new.txt b/base/src/main/assets/whats-new.txt index 4a199f4438..4af0dd8f1b 100644 --- a/base/src/main/assets/whats-new.txt +++ b/base/src/main/assets/whats-new.txt @@ -1,5 +1,5 @@ ✨ Screen-off remapping -You can now remap ALL buttons when the screen is off (including the power button) for free with PRO mode. +You can now remap ALL buttons when the screen is off (including the power button) for free with Expert mode. 🎯 New Actions • Run shell commands 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 index ac3706a965..78ba5f4ad1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/ActivityViewModel.kt @@ -29,9 +29,9 @@ class ActivityViewModel @Inject constructor( setupAccessibilityServiceDelegate.showCantFindAccessibilitySettingsDialog() } - fun launchProModeSetup() { + fun launchExpertModeSetup() { viewModelScope.launch { - navigate("pro_mode_setup", NavDestination.ProModeSetup) + navigate("expert_mode_setup", NavDestination.ExpertModeSetup) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt index 0ea1d4b5c2..e989b9748c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseKeyMapperApp.kt @@ -16,8 +16,9 @@ import androidx.lifecycle.LifecycleObserver import androidx.lifecycle.OnLifecycleEvent import androidx.lifecycle.ProcessLifecycleOwner import androidx.multidex.MultiDexApplication +import io.github.sds100.keymapper.base.expertmode.SystemBridgeAutoStarter import io.github.sds100.keymapper.base.logging.KeyMapperLoggingTree -import io.github.sds100.keymapper.base.promode.SystemBridgeAutoStarter +import io.github.sds100.keymapper.base.logging.SystemBridgeLogger import io.github.sds100.keymapper.base.settings.Theme import io.github.sds100.keymapper.base.system.accessibility.AccessibilityServiceAdapterImpl import io.github.sds100.keymapper.base.system.notifications.NotificationController @@ -90,6 +91,9 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { @Inject lateinit var systemBridgeConnectionManager: SystemBridgeConnectionManagerImpl + @Inject + lateinit var systemBridgeLogger: SystemBridgeLogger + private val processLifecycleOwner by lazy { ProcessLifecycleOwner.get() } private val userManager: UserManager? by lazy { getSystemService() } @@ -105,12 +109,6 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { when (intent.action) { Intent.ACTION_SHUTDOWN -> { Timber.i("Clean shutdown") - settingsRepository.set(Keys.isCleanShutdown, true) - - // Block until the value is persisted. - runBlocking { - settingsRepository.get(Keys.isCleanShutdown).first { it == true } - } } } } @@ -192,20 +190,22 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { notificationController.init() - processLifecycleOwner.lifecycle.addObserver(object : LifecycleObserver { - @Suppress("DEPRECATION") - @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() + processLifecycleOwner.lifecycle.addObserver( + object : LifecycleObserver { + @Suppress("DEPRECATION") + @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 -> @@ -234,6 +234,11 @@ abstract class BaseKeyMapperApp : MultiDexApplication() { if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeAutoStarter.init() + // Initialize SystemBridgeLogger to start receiving log messages from SystemBridge. + // Using Lazy<> to avoid circular dependency issues and ensure it's only created + // when the API level requirement is met. + systemBridgeLogger.start() + appCoroutineScope.launch { systemBridgeConnectionManager.connectionState.collect { state -> if (state is SystemBridgeConnectionState.Connected) { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt index d316fbc58a..b726317db1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainActivity.kt @@ -48,7 +48,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.launch -import timber.log.Timber abstract class BaseMainActivity : AppCompatActivity() { @@ -192,10 +191,6 @@ abstract class BaseMainActivity : AppCompatActivity() { override fun onResume() { super.onResume() - 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 @@ -259,7 +254,7 @@ abstract class BaseMainActivity : AppCompatActivity() { } ACTION_START_SYSTEM_BRIDGE -> { - viewModel.launchProModeSetup() + viewModel.launchExpertModeSetup() // Only clear the intent if it is handled in case it is used elsewhere this.intent = null 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 index 7dbe5c9a35..0b22cc858e 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseMainNavHost.kt @@ -25,11 +25,11 @@ 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.expertmode.ExpertModeScreen +import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupScreen import io.github.sds100.keymapper.base.logging.LogScreen import io.github.sds100.keymapper.base.onboarding.HandleAccessibilityServiceDialogs import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegateImpl -import io.github.sds100.keymapper.base.promode.ProModeScreen -import io.github.sds100.keymapper.base.promode.ProModeSetupScreen import io.github.sds100.keymapper.base.settings.AutomaticChangeImeSettingsScreen import io.github.sds100.keymapper.base.settings.DefaultOptionsSettingsScreen import io.github.sds100.keymapper.base.settings.SettingsScreen @@ -135,8 +135,8 @@ fun BaseMainNavHost( ) } - composable { - ProModeScreen( + composable { + ExpertModeScreen( modifier = Modifier .fillMaxSize() .windowInsetsPadding( @@ -151,8 +151,8 @@ fun BaseMainNavHost( ) } - composable { - ProModeSetupScreen( + composable { + ExpertModeSetupScreen( viewModel = hiltViewModel(), ) } 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 index dbc2efb661..435a7e3265 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseSingletonHiltModule.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseSingletonHiltModule.kt @@ -54,6 +54,8 @@ 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.Clock +import io.github.sds100.keymapper.common.utils.ClockImpl import io.github.sds100.keymapper.common.utils.DefaultUuidGenerator import io.github.sds100.keymapper.common.utils.UuidGenerator import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter @@ -202,4 +204,8 @@ abstract class BaseSingletonHiltModule { abstract fun bindSetupAccessibilityServiceDelegate( impl: SetupAccessibilityServiceDelegateImpl, ): SetupAccessibilityServiceDelegate + + @Binds + @Singleton + abstract fun bindClock(impl: ClockImpl): Clock } 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 index 22a957ad16..75ebbc90c7 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/BaseViewModelHiltModule.kt @@ -25,6 +25,10 @@ import io.github.sds100.keymapper.base.constraints.ConfigConstraintsUseCaseImpl import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCase import io.github.sds100.keymapper.base.constraints.CreateConstraintUseCaseImpl import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase +import io.github.sds100.keymapper.base.expertmode.ExpertModeSetupDelegateImpl +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupDelegate +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCase +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupUseCaseImpl import io.github.sds100.keymapper.base.home.ListKeyMapsUseCase import io.github.sds100.keymapper.base.home.ListKeyMapsUseCaseImpl import io.github.sds100.keymapper.base.home.ShowHomeScreenAlertsUseCase @@ -37,8 +41,6 @@ import io.github.sds100.keymapper.base.logging.ShareLogcatUseCase import io.github.sds100.keymapper.base.logging.ShareLogcatUseCaseImpl import io.github.sds100.keymapper.base.onboarding.OnboardingTipDelegate import io.github.sds100.keymapper.base.onboarding.OnboardingTipDelegateImpl -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupUseCase -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupUseCaseImpl import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCase import io.github.sds100.keymapper.base.settings.ConfigSettingsUseCaseImpl import io.github.sds100.keymapper.base.shortcuts.CreateKeyMapShortcutUseCase @@ -151,7 +153,7 @@ abstract class BaseViewModelHiltModule { @Binds @ViewModelScoped - abstract fun bindProModeSetupUseCase( + abstract fun bindExpertModeSetupUseCase( impl: SystemBridgeSetupUseCaseImpl, ): SystemBridgeSetupUseCase @@ -192,4 +194,10 @@ abstract class BaseViewModelHiltModule { abstract fun bindFixKeyEventActionDelegate( impl: FixKeyEventActionDelegateImpl, ): FixKeyEventActionDelegate + + @Binds + @ViewModelScoped + abstract fun bindExpertModeSetupDelegate( + impl: ExpertModeSetupDelegateImpl, + ): SystemBridgeSetupDelegate } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt index b051aa498d..0385eedbb0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionUtils.kt @@ -121,7 +121,6 @@ object ActionUtils { ActionId.SWIPE_SCREEN -> ActionCategory.INPUT ActionId.PINCH_SCREEN -> ActionCategory.INPUT ActionId.TEXT -> ActionCategory.INPUT - ActionId.OPEN_VOICE_ASSISTANT -> ActionCategory.APPS ActionId.OPEN_DEVICE_ASSISTANT -> ActionCategory.APPS ActionId.OPEN_CAMERA -> ActionCategory.APPS @@ -132,30 +131,24 @@ object ActionUtils { ActionId.URL -> ActionCategory.APPS ActionId.HTTP_REQUEST -> ActionCategory.APPS ActionId.SHELL_COMMAND -> ActionCategory.APPS - ActionId.TOGGLE_WIFI -> ActionCategory.CONNECTIVITY ActionId.ENABLE_WIFI -> ActionCategory.CONNECTIVITY ActionId.DISABLE_WIFI -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_BLUETOOTH -> ActionCategory.CONNECTIVITY ActionId.ENABLE_BLUETOOTH -> ActionCategory.CONNECTIVITY ActionId.DISABLE_BLUETOOTH -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY ActionId.ENABLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY ActionId.DISABLE_MOBILE_DATA -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_HOTSPOT -> ActionCategory.CONNECTIVITY ActionId.ENABLE_HOTSPOT -> ActionCategory.CONNECTIVITY ActionId.DISABLE_HOTSPOT -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.DISABLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.ENABLE_AUTO_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.INCREASE_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.DECREASE_BRIGHTNESS -> ActionCategory.DISPLAY ActionId.SCREENSHOT -> ActionCategory.DISPLAY - ActionId.TOGGLE_AUTO_ROTATE -> ActionCategory.INTERFACE ActionId.ENABLE_AUTO_ROTATE -> ActionCategory.INTERFACE ActionId.DISABLE_AUTO_ROTATE -> ActionCategory.INTERFACE @@ -163,7 +156,6 @@ object ActionUtils { ActionId.LANDSCAPE_MODE -> ActionCategory.INTERFACE ActionId.SWITCH_ORIENTATION -> ActionCategory.INTERFACE ActionId.CYCLE_ROTATIONS -> ActionCategory.INTERFACE - ActionId.VOLUME_UP -> ActionCategory.VOLUME ActionId.VOLUME_DOWN -> ActionCategory.VOLUME ActionId.VOLUME_SHOW_DIALOG -> ActionCategory.VOLUME @@ -181,13 +173,11 @@ object ActionUtils { ActionId.MUTE_MICROPHONE -> ActionCategory.VOLUME ActionId.UNMUTE_MICROPHONE -> ActionCategory.VOLUME ActionId.TOGGLE_MUTE_MICROPHONE -> ActionCategory.VOLUME - ActionId.EXPAND_NOTIFICATION_DRAWER -> ActionCategory.NAVIGATION ActionId.TOGGLE_NOTIFICATION_DRAWER -> ActionCategory.NAVIGATION ActionId.EXPAND_QUICK_SETTINGS -> ActionCategory.NAVIGATION ActionId.TOGGLE_QUICK_SETTINGS -> ActionCategory.NAVIGATION ActionId.COLLAPSE_STATUS_BAR -> ActionCategory.NAVIGATION - ActionId.SOUND -> ActionCategory.MEDIA ActionId.PAUSE_MEDIA -> ActionCategory.MEDIA ActionId.PAUSE_MEDIA_PACKAGE -> ActionCategory.MEDIA @@ -209,27 +199,22 @@ object ActionUtils { ActionId.STEP_FORWARD_PACKAGE -> ActionCategory.MEDIA ActionId.STEP_BACKWARD -> ActionCategory.MEDIA ActionId.STEP_BACKWARD_PACKAGE -> ActionCategory.MEDIA - ActionId.GO_BACK -> ActionCategory.NAVIGATION ActionId.GO_HOME -> ActionCategory.NAVIGATION ActionId.OPEN_RECENTS -> ActionCategory.NAVIGATION ActionId.TOGGLE_SPLIT_SCREEN -> ActionCategory.NAVIGATION ActionId.GO_LAST_APP -> ActionCategory.NAVIGATION ActionId.OPEN_MENU -> ActionCategory.NAVIGATION - ActionId.TOGGLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.ENABLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.DISABLE_FLASHLIGHT -> ActionCategory.FLASHLIGHT ActionId.CHANGE_FLASHLIGHT_STRENGTH -> ActionCategory.FLASHLIGHT - ActionId.ENABLE_NFC -> ActionCategory.CONNECTIVITY ActionId.DISABLE_NFC -> ActionCategory.CONNECTIVITY ActionId.TOGGLE_NFC -> ActionCategory.CONNECTIVITY - ActionId.TOGGLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY ActionId.ENABLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY ActionId.DISABLE_AIRPLANE_MODE -> ActionCategory.CONNECTIVITY - ActionId.TEXT_CUT -> ActionCategory.KEYBOARD ActionId.TEXT_COPY -> ActionCategory.KEYBOARD ActionId.TEXT_PASTE -> ActionCategory.KEYBOARD @@ -240,162 +225,283 @@ object ActionUtils { ActionId.SHOW_KEYBOARD_PICKER -> ActionCategory.KEYBOARD ActionId.SELECT_WORD_AT_CURSOR -> ActionCategory.KEYBOARD ActionId.SWITCH_KEYBOARD -> ActionCategory.KEYBOARD - ActionId.LOCK_DEVICE -> ActionCategory.INTERFACE ActionId.POWER_ON_OFF_DEVICE -> ActionCategory.INTERFACE ActionId.SECURE_LOCK_DEVICE -> ActionCategory.INTERFACE ActionId.SHOW_POWER_MENU -> ActionCategory.INTERFACE - ActionId.PHONE_CALL -> ActionCategory.TELEPHONY ActionId.ANSWER_PHONE_CALL -> ActionCategory.TELEPHONY ActionId.END_PHONE_CALL -> ActionCategory.TELEPHONY ActionId.SEND_SMS -> ActionCategory.TELEPHONY ActionId.COMPOSE_SMS -> ActionCategory.TELEPHONY - ActionId.DISMISS_MOST_RECENT_NOTIFICATION -> ActionCategory.NOTIFICATIONS ActionId.DISMISS_ALL_NOTIFICATIONS -> ActionCategory.NOTIFICATIONS ActionId.CREATE_NOTIFICATION -> ActionCategory.NOTIFICATIONS ActionId.DEVICE_CONTROLS -> ActionCategory.APPS - ActionId.INTERACT_UI_ELEMENT -> ActionCategory.APPS ActionId.FORCE_STOP_APP -> ActionCategory.APPS ActionId.CLEAR_RECENT_APP -> ActionCategory.APPS ActionId.MODIFY_SETTING -> ActionCategory.APPS - ActionId.CONSUME_KEY_EVENT -> ActionCategory.SPECIAL } @StringRes fun getTitle(id: ActionId): Int = when (id) { ActionId.TOGGLE_WIFI -> R.string.action_toggle_wifi + ActionId.ENABLE_WIFI -> R.string.action_enable_wifi + ActionId.DISABLE_WIFI -> R.string.action_disable_wifi + ActionId.TOGGLE_BLUETOOTH -> R.string.action_toggle_bluetooth + ActionId.ENABLE_BLUETOOTH -> R.string.action_enable_bluetooth + ActionId.DISABLE_BLUETOOTH -> R.string.action_disable_bluetooth + ActionId.TOGGLE_MOBILE_DATA -> R.string.action_toggle_mobile_data + ActionId.ENABLE_MOBILE_DATA -> R.string.action_enable_mobile_data + ActionId.DISABLE_MOBILE_DATA -> R.string.action_disable_mobile_data + ActionId.TOGGLE_AUTO_BRIGHTNESS -> R.string.action_toggle_auto_brightness + ActionId.DISABLE_AUTO_BRIGHTNESS -> R.string.action_disable_auto_brightness + ActionId.ENABLE_AUTO_BRIGHTNESS -> R.string.action_enable_auto_brightness + ActionId.INCREASE_BRIGHTNESS -> R.string.action_increase_brightness + ActionId.DECREASE_BRIGHTNESS -> R.string.action_decrease_brightness + ActionId.TOGGLE_AUTO_ROTATE -> R.string.action_toggle_auto_rotate + ActionId.ENABLE_AUTO_ROTATE -> R.string.action_enable_auto_rotate + ActionId.DISABLE_AUTO_ROTATE -> R.string.action_disable_auto_rotate + ActionId.PORTRAIT_MODE -> R.string.action_portrait_mode + ActionId.LANDSCAPE_MODE -> R.string.action_landscape_mode + ActionId.SWITCH_ORIENTATION -> R.string.action_switch_orientation + ActionId.CYCLE_ROTATIONS -> R.string.action_cycle_rotations + ActionId.VOLUME_UP -> R.string.action_volume_up + ActionId.VOLUME_DOWN -> R.string.action_volume_down + ActionId.VOLUME_SHOW_DIALOG -> R.string.action_volume_show_dialog + ActionId.VOLUME_DECREASE_STREAM -> R.string.action_decrease_stream + ActionId.VOLUME_INCREASE_STREAM -> R.string.action_increase_stream + ActionId.CYCLE_RINGER_MODE -> R.string.action_cycle_ringer_mode + ActionId.CHANGE_RINGER_MODE -> R.string.action_change_ringer_mode + ActionId.CYCLE_VIBRATE_RING -> R.string.action_cycle_vibrate_ring + ActionId.TOGGLE_DND_MODE -> R.string.action_toggle_dnd_mode + ActionId.ENABLE_DND_MODE -> R.string.action_enable_dnd_mode + ActionId.DISABLE_DND_MODE -> R.string.action_disable_dnd_mode + ActionId.VOLUME_UNMUTE -> R.string.action_volume_unmute + ActionId.VOLUME_MUTE -> R.string.action_volume_mute + ActionId.VOLUME_TOGGLE_MUTE -> R.string.action_toggle_mute + ActionId.MUTE_MICROPHONE -> R.string.action_mute_microphone + ActionId.UNMUTE_MICROPHONE -> R.string.action_unmute_microphone + ActionId.TOGGLE_MUTE_MICROPHONE -> R.string.action_toggle_mute_microphone + ActionId.EXPAND_NOTIFICATION_DRAWER -> R.string.action_expand_notification_drawer + ActionId.TOGGLE_NOTIFICATION_DRAWER -> R.string.action_toggle_notification_drawer + ActionId.EXPAND_QUICK_SETTINGS -> R.string.action_expand_quick_settings + ActionId.TOGGLE_QUICK_SETTINGS -> R.string.action_toggle_quick_settings + ActionId.COLLAPSE_STATUS_BAR -> R.string.action_collapse_status_bar + ActionId.PAUSE_MEDIA -> R.string.action_pause_media + ActionId.PAUSE_MEDIA_PACKAGE -> R.string.action_pause_media_package + ActionId.PLAY_MEDIA -> R.string.action_play_media + ActionId.PLAY_MEDIA_PACKAGE -> R.string.action_play_media_package + ActionId.PLAY_PAUSE_MEDIA -> R.string.action_play_pause_media + ActionId.PLAY_PAUSE_MEDIA_PACKAGE -> R.string.action_play_pause_media_package + ActionId.NEXT_TRACK -> R.string.action_next_track + ActionId.NEXT_TRACK_PACKAGE -> R.string.action_next_track_package + ActionId.PREVIOUS_TRACK -> R.string.action_previous_track + ActionId.PREVIOUS_TRACK_PACKAGE -> R.string.action_previous_track_package + ActionId.FAST_FORWARD -> R.string.action_fast_forward + ActionId.FAST_FORWARD_PACKAGE -> R.string.action_fast_forward_package + ActionId.REWIND -> R.string.action_rewind + ActionId.REWIND_PACKAGE -> R.string.action_rewind_package + ActionId.STOP_MEDIA -> R.string.action_stop_media + ActionId.STOP_MEDIA_PACKAGE -> R.string.action_stop_media_package + ActionId.STEP_FORWARD -> R.string.action_step_forward_media + ActionId.STEP_FORWARD_PACKAGE -> R.string.action_step_forward_media_package + ActionId.STEP_BACKWARD -> R.string.action_step_backward_media + ActionId.STEP_BACKWARD_PACKAGE -> R.string.action_step_backward_media_package + ActionId.GO_BACK -> R.string.action_go_back + ActionId.GO_HOME -> R.string.action_go_home + ActionId.OPEN_RECENTS -> R.string.action_open_recents + ActionId.TOGGLE_SPLIT_SCREEN -> R.string.action_toggle_split_screen + ActionId.GO_LAST_APP -> R.string.action_go_last_app + ActionId.OPEN_MENU -> R.string.action_open_menu + ActionId.TOGGLE_FLASHLIGHT -> R.string.action_toggle_flashlight + ActionId.ENABLE_FLASHLIGHT -> R.string.action_enable_flashlight + ActionId.DISABLE_FLASHLIGHT -> R.string.action_disable_flashlight + ActionId.CHANGE_FLASHLIGHT_STRENGTH -> R.string.action_flashlight_change_strength + ActionId.ENABLE_NFC -> R.string.action_nfc_enable + ActionId.DISABLE_NFC -> R.string.action_nfc_disable + ActionId.TOGGLE_NFC -> R.string.action_nfc_toggle + ActionId.MOVE_CURSOR -> R.string.action_move_cursor + ActionId.TOGGLE_KEYBOARD -> R.string.action_toggle_keyboard + ActionId.SHOW_KEYBOARD -> R.string.action_show_keyboard + ActionId.HIDE_KEYBOARD -> R.string.action_hide_keyboard + ActionId.SHOW_KEYBOARD_PICKER -> R.string.action_show_keyboard_picker + ActionId.TEXT_CUT -> R.string.action_text_cut + ActionId.TEXT_COPY -> R.string.action_text_copy + ActionId.TEXT_PASTE -> R.string.action_text_paste + ActionId.SELECT_WORD_AT_CURSOR -> R.string.action_select_word_at_cursor + ActionId.SWITCH_KEYBOARD -> R.string.action_switch_keyboard + ActionId.TOGGLE_AIRPLANE_MODE -> R.string.action_toggle_airplane_mode + ActionId.ENABLE_AIRPLANE_MODE -> R.string.action_enable_airplane_mode + ActionId.DISABLE_AIRPLANE_MODE -> R.string.action_disable_airplane_mode + ActionId.SCREENSHOT -> R.string.action_screenshot + ActionId.OPEN_VOICE_ASSISTANT -> R.string.action_open_assistant + ActionId.OPEN_DEVICE_ASSISTANT -> R.string.action_open_device_assistant + ActionId.OPEN_CAMERA -> R.string.action_open_camera + ActionId.LOCK_DEVICE -> R.string.action_lock_device + ActionId.POWER_ON_OFF_DEVICE -> R.string.action_power_on_off_device + ActionId.SECURE_LOCK_DEVICE -> R.string.action_secure_lock_device + ActionId.CONSUME_KEY_EVENT -> R.string.action_consume_keyevent + ActionId.OPEN_SETTINGS -> R.string.action_open_settings + ActionId.SHOW_POWER_MENU -> R.string.action_show_power_menu + ActionId.APP -> R.string.action_open_app + ActionId.APP_SHORTCUT -> R.string.action_open_app_shortcut + ActionId.KEY_CODE -> R.string.action_input_key_code + ActionId.KEY_EVENT -> R.string.action_input_key_event + ActionId.TAP_SCREEN -> R.string.action_tap_screen + ActionId.SWIPE_SCREEN -> R.string.action_swipe_screen + ActionId.PINCH_SCREEN -> R.string.action_pinch_screen + ActionId.TEXT -> R.string.action_input_text + ActionId.URL -> R.string.action_open_url + ActionId.INTENT -> R.string.action_send_intent + ActionId.PHONE_CALL -> R.string.action_phone_call + ActionId.SOUND -> R.string.action_play_sound + ActionId.DISMISS_MOST_RECENT_NOTIFICATION -> R.string.action_dismiss_most_recent_notification + ActionId.DISMISS_ALL_NOTIFICATIONS -> R.string.action_dismiss_all_notifications + ActionId.CREATE_NOTIFICATION -> R.string.action_create_notification + ActionId.ANSWER_PHONE_CALL -> R.string.action_answer_call + ActionId.END_PHONE_CALL -> R.string.action_end_call + ActionId.SEND_SMS -> R.string.action_send_sms + ActionId.COMPOSE_SMS -> R.string.action_compose_sms + ActionId.DEVICE_CONTROLS -> R.string.action_device_controls + ActionId.HTTP_REQUEST -> R.string.action_http_request + ActionId.SHELL_COMMAND -> R.string.action_shell_command + ActionId.INTERACT_UI_ELEMENT -> R.string.action_interact_ui_element_title + ActionId.FORCE_STOP_APP -> R.string.action_force_stop_app + ActionId.CLEAR_RECENT_APP -> R.string.action_clear_recent_app ActionId.MODIFY_SETTING -> R.string.action_modify_setting + ActionId.TOGGLE_HOTSPOT -> R.string.action_toggle_hotspot + ActionId.ENABLE_HOTSPOT -> R.string.action_enable_hotspot + ActionId.DISABLE_HOTSPOT -> R.string.action_disable_hotspot } @@ -525,10 +631,13 @@ object ActionUtils { fun getMinApi(id: ActionId): Int = when (id) { ActionId.ANSWER_PHONE_CALL -> Build.VERSION_CODES.O + ActionId.END_PHONE_CALL -> Build.VERSION_CODES.P ActionId.TOGGLE_SPLIT_SCREEN -> Build.VERSION_CODES.N + ActionId.GO_LAST_APP -> Build.VERSION_CODES.N + ActionId.TAP_SCREEN -> Build.VERSION_CODES.N ActionId.VOLUME_MUTE, @@ -562,6 +671,7 @@ object ActionUtils { -> Build.VERSION_CODES.JELLY_BEAN_MR2 ActionId.SHOW_POWER_MENU -> Build.VERSION_CODES.LOLLIPOP + ActionId.DEVICE_CONTROLS -> Build.VERSION_CODES.S // It could be supported on older versions but system bridge min API is Q and its extra @@ -761,6 +871,7 @@ object ActionUtils { } ActionId.SECURE_LOCK_DEVICE -> return listOf(Permission.DEVICE_ADMIN) + ActionId.POWER_ON_OFF_DEVICE -> return if (isSystemBridgeSupported) { emptyList() } else { @@ -923,7 +1034,6 @@ object ActionUtils { ActionId.INTERACT_UI_ELEMENT -> KeyMapperIcons.JumpToElement ActionId.FORCE_STOP_APP -> Icons.Outlined.Dangerous ActionId.CLEAR_RECENT_APP -> Icons.Outlined.VerticalSplit - ActionId.MODIFY_SETTING -> Icons.Outlined.Settings ActionId.TOGGLE_HOTSPOT -> Icons.Outlined.WifiTethering ActionId.ENABLE_HOTSPOT -> Icons.Outlined.WifiTethering @@ -933,6 +1043,7 @@ object ActionUtils { fun ActionData.canBeHeldDown(): Boolean = when (this) { is ActionData.InputKeyEvent -> true + is ActionData.TapScreen -> Build.VERSION.SDK_INT >= Build.VERSION_CODES.O else -> false } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt index 55c61681ab..b7fec30f2c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt @@ -79,11 +79,11 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod sheetState = sheetState, onDismissRequest = viewModel::dismissFixKeyEventActionBottomSheet, onEnableAccessibilityServiceClick = viewModel::onEnableAccessibilityServiceClick, - onEnableProModeClick = viewModel::onEnableProModeForKeyEventActionsClick, + onEnableExpertModeClick = viewModel::onEnableExpertModeForKeyEventActionsClick, onEnableInputMethodClick = viewModel::onEnableImeClick, onChooseInputMethodClick = viewModel::onChooseImeClick, onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet, - onSelectProMode = viewModel::onSelectProMode, + onSelectExpertMode = viewModel::onSelectExpertMode, onSelectInputMethod = viewModel::onSelectInputMethod, onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange, ) @@ -150,6 +150,7 @@ private fun ActionsScreen( when (state) { State.Loading -> Loading() + is State.Data -> Surface(modifier = modifier) { Column { Spacer(Modifier.height(8.dp)) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt index 954e188961..e8262c6c42 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModel.kt @@ -1,17 +1,18 @@ package io.github.sds100.keymapper.base.actions import android.os.Build -import android.util.Base64 import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.R +import io.github.sds100.keymapper.base.utils.ExpertModeStatus 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.ResourceProvider import io.github.sds100.keymapper.common.models.ShellExecutionMode import io.github.sds100.keymapper.common.models.isExecuting import io.github.sds100.keymapper.common.utils.Constants @@ -20,6 +21,7 @@ import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import java.util.Base64 import javax.inject.Inject import kotlinx.coroutines.Job import kotlinx.coroutines.flow.map @@ -32,7 +34,9 @@ class ConfigShellCommandViewModel @Inject constructor( private val navigationProvider: NavigationProvider, private val systemBridgeConnectionManager: SystemBridgeConnectionManager, private val preferenceRepository: PreferenceRepository, -) : ViewModel() { + resourceProvider: ResourceProvider, +) : ViewModel(), + ResourceProvider by resourceProvider { var state: ShellCommandActionState by mutableStateOf(ShellCommandActionState()) private set @@ -40,16 +44,16 @@ class ConfigShellCommandViewModel @Inject constructor( private var testJob: Job? = null init { - // Update ProModeStatus in state + // Update ExpertModeStatus in state if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { viewModelScope.launch { systemBridgeConnectionManager.connectionState.map { connectionState -> when (connectionState) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } - }.collect { proModeStatus -> - state = state.copy(proModeStatus = proModeStatus) + }.collect { expertModeStatus -> + state = state.copy(expertModeStatus = expertModeStatus) } } } @@ -68,11 +72,11 @@ class ConfigShellCommandViewModel @Inject constructor( } fun onDescriptionChanged(newDescription: String) { - state = state.copy(description = newDescription) + state = state.copy(description = newDescription, descriptionError = null) } fun onCommandChanged(newCommand: String) { - state = state.copy(command = newCommand) + state = state.copy(command = newCommand, commandError = null) saveScriptText(newCommand) } @@ -84,9 +88,15 @@ class ConfigShellCommandViewModel @Inject constructor( state = state.copy(timeoutSeconds = newTimeoutSeconds) } - fun onTestClick() { + fun onTestClick(): Boolean { testJob?.cancel() + val commandError = validateCommand(state.command) + if (commandError != null) { + state = state.copy(commandError = commandError) + return false + } + state = state.copy( isRunning = true, testResult = null, @@ -95,6 +105,8 @@ class ConfigShellCommandViewModel @Inject constructor( testJob = viewModelScope.launch { testCommand() } + + return true } private suspend fun testCommand() { @@ -119,7 +131,21 @@ class ConfigShellCommandViewModel @Inject constructor( ) } - fun onDoneClick() { + fun onDoneClick(): Boolean { + val commandError = validateCommand(state.command) + if (commandError != null) { + state = state.copy(commandError = commandError) + return false + } + + if (state.description.isBlank()) { + state = state.copy( + descriptionError = getString(R.string.error_cant_be_empty), + ) + + return false + } + val action = ActionData.ShellCommand( description = state.description, command = state.command, @@ -133,6 +159,8 @@ class ConfigShellCommandViewModel @Inject constructor( viewModelScope.launch { navigationProvider.popBackStackWithResult(Json.encodeToString(action)) } + + return true } fun onCancelClick() { @@ -144,15 +172,33 @@ class ConfigShellCommandViewModel @Inject constructor( } } - fun onSetupProModeClick() { + fun onSetupExpertModeClick() { viewModelScope.launch { - navigationProvider.navigate("shell_command_setup_pro_mode", NavDestination.ProModeSetup) + navigationProvider.navigate( + "shell_command_setup_expert_mode", + NavDestination.ExpertModeSetup, + ) + } + } + + /** + * @return the error message. + */ + private fun validateCommand(command: String): String? { + if (state.command.isBlank()) { + return getString(R.string.action_shell_command_command_empty_error) + } + + if (state.command.trimStart().startsWith("adb shell")) { + return getString(R.string.action_shell_command_adb_shell_error) } + + return null } private fun saveScriptText(scriptText: String) { viewModelScope.launch { - val encodedText = Base64.encodeToString(scriptText.toByteArray(), Base64.DEFAULT).trim() + val encodedText = Base64.getEncoder().encodeToString(scriptText.toByteArray()).trim() preferenceRepository.set(Keys.shellCommandScriptText, encodedText) } } @@ -162,7 +208,7 @@ class ConfigShellCommandViewModel @Inject constructor( preferenceRepository.get(Keys.shellCommandScriptText).collect { savedScriptText -> if (savedScriptText != null && state.command.isEmpty()) { try { - val decodedText = String(Base64.decode(savedScriptText, Base64.DEFAULT)) + val decodedText = String(Base64.getDecoder().decode(savedScriptText)) state = state.copy(command = decodedText) } catch (e: Exception) { // If decoding fails, ignore the saved text diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionTriggerDevice.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionTriggerDevice.kt new file mode 100644 index 0000000000..f478803eba --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionTriggerDevice.kt @@ -0,0 +1,13 @@ +package io.github.sds100.keymapper.base.actions + +/** + * Identifies which device triggered the action. + */ +sealed class PerformActionTriggerDevice { + /** + * The action was triggered by an evdev-level input device. + */ + data class Evdev(val deviceId: Int) : PerformActionTriggerDevice() + + data object Default : PerformActionTriggerDevice() +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt index 25e9764bf9..8b1dd81c9d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCase.kt @@ -28,16 +28,13 @@ import io.github.sds100.keymapper.common.utils.KMError.SdkVersionTooLow 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 @@ -52,7 +49,6 @@ import io.github.sds100.keymapper.system.devices.DevicesAdapter import io.github.sds100.keymapper.system.display.DisplayAdapter import io.github.sds100.keymapper.system.files.FileAdapter import io.github.sds100.keymapper.system.files.FileUtils -import io.github.sds100.keymapper.system.inputevents.KeyEventUtils import io.github.sds100.keymapper.system.inputevents.Scancode import io.github.sds100.keymapper.system.inputmethod.InputMethodAdapter import io.github.sds100.keymapper.system.intents.IntentAdapter @@ -78,12 +74,9 @@ import kotlin.math.absoluteValue import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.delay import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.runBlocking import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber @@ -143,15 +136,19 @@ class PerformActionsUseCaseImpl @AssistedInject constructor( ) } - private val injectKeyEventsWithSystemBridge: StateFlow = - settingsRepository.get(Keys.keyEventActionsUseSystemBridge) - .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } - .stateIn(coroutineScope, SharingStarted.Eagerly, false) + private val performKeyEventActionDelegate: PerformKeyEventActionDelegate = + PerformKeyEventActionDelegate( + coroutineScope, + settingsRepository, + inputEventHub, + devicesAdapter, + ) override suspend fun perform( action: ActionData, inputEventAction: InputEventAction, keyMetaState: Int, + device: PerformActionTriggerDevice, ) { /** * Is null if the action is being performed asynchronously @@ -172,48 +169,12 @@ class PerformActionsUseCaseImpl @AssistedInject constructor( } is ActionData.InputKeyEvent -> { - val deviceId: Int = getDeviceIdForKeyEventAction(action) - - // See issue #1683. Some apps ignore key events which do not have a source. - val source = when { - KeyEventUtils.isDpadKeyCode(action.keyCode) -> InputDevice.SOURCE_DPAD - KeyEventUtils.isGamepadButton(action.keyCode) -> InputDevice.SOURCE_GAMEPAD - else -> InputDevice.SOURCE_KEYBOARD - } - - val firstInputAction = if (inputEventAction == InputEventAction.UP) { - KeyEvent.ACTION_UP - } else { - KeyEvent.ACTION_DOWN - } - - val model = InjectKeyEventModel( - keyCode = action.keyCode, - action = firstInputAction, - metaState = keyMetaState.withFlag(action.metaState), - deviceId = deviceId, - source = source, - repeatCount = 0, - scanCode = 0, + result = performKeyEventActionDelegate.perform( + action, + inputEventAction, + keyMetaState, + device, ) - - if (inputEventAction == InputEventAction.DOWN_UP) { - result = inputEventHub.injectKeyEvent( - model, - useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, - ) - .then { - inputEventHub.injectKeyEvent( - model.copy(action = KeyEvent.ACTION_UP), - useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, - ) - } - } else { - result = inputEventHub.injectKeyEvent( - model, - useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, - ) - } } is ActionData.PhoneCall -> { @@ -1108,53 +1069,6 @@ class PerformActionsUseCaseImpl @AssistedInject constructor( .map { it ?: PreferenceDefaults.HOLD_DOWN_DURATION } .map { it.toLong() } - private fun getDeviceIdForKeyEventAction(action: ActionData.InputKeyEvent): Int { - if (action.device?.descriptor == null) { - // automatically select a game controller as the input device for game controller key events - - if (KeyEventUtils.isGamepadKeyCode(action.keyCode)) { - devicesAdapter.connectedInputDevices.value.ifIsData { inputDevices -> - val device = inputDevices.find { it.isGameController } - - if (device != null) { - return device.id - } - } - } - - return 0 - } - - val inputDevices = devicesAdapter.connectedInputDevices.value - - val devicesWithSameDescriptor = - inputDevices.dataOrNull() - ?.filter { it.descriptor == action.device.descriptor } - ?: emptyList() - - if (devicesWithSameDescriptor.isEmpty()) { - return -1 - } - - if (devicesWithSameDescriptor.size == 1) { - return devicesWithSameDescriptor[0].id - } - - /* - if there are multiple devices use the device that supports the key - code. if none do then use the first one - */ - val deviceThatHasKey = devicesWithSameDescriptor.singleOrNull { - devicesAdapter.deviceHasKey(it.id, action.keyCode) - } - - val device = deviceThatHasKey - ?: devicesWithSameDescriptor.singleOrNull { it.name == action.device.name } - ?: devicesWithSameDescriptor[0] - - return device.id - } - private fun closeStatusBarShade(): KMResult<*> { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return service @@ -1233,6 +1147,7 @@ interface PerformActionsUseCase { action: ActionData, inputEventAction: InputEventAction = InputEventAction.DOWN_UP, keyMetaState: Int = 0, + device: PerformActionTriggerDevice = PerformActionTriggerDevice.Default, ) fun getErrorSnapshot(): ActionErrorSnapshot diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegate.kt new file mode 100644 index 0000000000..a059059ec3 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegate.kt @@ -0,0 +1,206 @@ +package io.github.sds100.keymapper.base.actions + +import android.view.InputDevice +import android.view.KeyEvent +import io.github.sds100.keymapper.base.input.InjectKeyEventModel +import io.github.sds100.keymapper.base.input.InputEventHub +import io.github.sds100.keymapper.common.utils.InputEventAction +import io.github.sds100.keymapper.common.utils.KMResult +import io.github.sds100.keymapper.common.utils.dataOrNull +import io.github.sds100.keymapper.common.utils.ifIsData +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.devices.DevicesAdapter +import io.github.sds100.keymapper.system.inputevents.KeyEventUtils +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn + +class PerformKeyEventActionDelegate( + private val coroutineScope: CoroutineScope, + private val settingsRepository: PreferenceRepository, + private val inputEventHub: InputEventHub, + private val devicesAdapter: DevicesAdapter, +) { + private val injectKeyEventsWithSystemBridge: StateFlow = + settingsRepository.get(Keys.keyEventActionsUseSystemBridge) + .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } + .stateIn(coroutineScope, SharingStarted.Eagerly, false) + + suspend fun perform( + action: ActionData.InputKeyEvent, + inputEventAction: InputEventAction, + keyMetaState: Int, + triggerDevice: PerformActionTriggerDevice, + ): KMResult { + // Only input evdev event if the device is grabbed. It may not be grabbed + // if the device is disconnected. + if (injectKeyEventsWithSystemBridge.value && + triggerDevice is PerformActionTriggerDevice.Evdev && + (action.device == null || isActionDeviceGrabbed(action.device)) + ) { + return injectEvdevEvent(inputEventAction, triggerDevice.deviceId, action) + } + + val deviceId: Int = getDeviceIdForKeyEventAction(action) + + // If the device that the user specified in the action can not be found + // then fallback to evdev injection. + if (injectKeyEventsWithSystemBridge.value && + deviceId == -1 && + triggerDevice is PerformActionTriggerDevice.Evdev + ) { + return injectEvdevEvent(inputEventAction, triggerDevice.deviceId, action) + } + + // See issue #1683. Some apps ignore key events which do not have a source. + val source = when { + KeyEventUtils.isDpadKeyCode(action.keyCode) -> InputDevice.SOURCE_DPAD + KeyEventUtils.isGamepadButton(action.keyCode) -> InputDevice.SOURCE_GAMEPAD + else -> InputDevice.SOURCE_KEYBOARD + } + + val firstInputAction = if (inputEventAction == InputEventAction.UP) { + KeyEvent.ACTION_UP + } else { + KeyEvent.ACTION_DOWN + } + + val model = InjectKeyEventModel( + keyCode = action.keyCode, + action = firstInputAction, + metaState = keyMetaState.withFlag(action.metaState), + deviceId = deviceId, + source = source, + repeatCount = 0, + scanCode = 0, + ) + + return injectAndroidKeyEvent(inputEventAction, model) + } + + private fun isActionDeviceGrabbed(device: ActionData.InputKeyEvent.Device): Boolean { + return inputEventHub.getGrabbedDevices().any { it.name == device.name } + } + + private fun injectEvdevEvent( + inputEventAction: InputEventAction, + deviceId: Int, + action: ActionData.InputKeyEvent, + ): KMResult { + when (inputEventAction) { + InputEventAction.DOWN_UP -> { + return injectDownEvdevEvent( + deviceId, + action, + ).then { injectUpEvdevEvent(deviceId, action) } + } + + InputEventAction.DOWN -> return injectDownEvdevEvent( + deviceId, + action, + ) + + InputEventAction.UP -> return injectUpEvdevEvent( + deviceId, + action, + ) + } + } + + private fun injectDownEvdevEvent( + deviceId: Int, + action: ActionData.InputKeyEvent, + ): KMResult { + return inputEventHub.injectEvdevEventKeyCode( + deviceId = deviceId, + keyCode = action.keyCode, + value = 1, + ) + } + + private fun injectUpEvdevEvent( + deviceId: Int, + action: ActionData.InputKeyEvent, + ): KMResult { + return inputEventHub.injectEvdevEventKeyCode( + deviceId = deviceId, + action.keyCode, + value = 0, + ) + } + + private suspend fun injectAndroidKeyEvent( + inputEventAction: InputEventAction, + model: InjectKeyEventModel, + ): KMResult { + if (inputEventAction == InputEventAction.DOWN_UP) { + return inputEventHub.injectKeyEvent( + model, + useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, + ).then { + inputEventHub.injectKeyEvent( + model.copy(action = KeyEvent.ACTION_UP), + useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, + ) + } + } else { + return inputEventHub.injectKeyEvent( + model, + useSystemBridgeIfAvailable = injectKeyEventsWithSystemBridge.value, + ) + } + } + + private fun getDeviceIdForKeyEventAction(action: ActionData.InputKeyEvent): Int { + if (action.device?.descriptor == null) { + // automatically select a game controller as the input device for game controller key events + + if (KeyEventUtils.isGamepadKeyCode(action.keyCode)) { + devicesAdapter.connectedInputDevices.value.ifIsData { inputDevices -> + val device = inputDevices.find { it.isGameController } + + if (device != null) { + return device.id + } + } + } + + return 0 + } + + val inputDevices = devicesAdapter.connectedInputDevices.value + + val devicesWithSameDescriptor = + inputDevices.dataOrNull() + ?.filter { it.descriptor == action.device.descriptor } + ?: emptyList() + + if (devicesWithSameDescriptor.isEmpty()) { + return -1 + } + + if (devicesWithSameDescriptor.size == 1) { + return devicesWithSameDescriptor[0].id + } + + /* + if there are multiple devices use the device that supports the key + code. if none do then use the first one + */ + val deviceThatHasKey = devicesWithSameDescriptor.singleOrNull { + devicesAdapter.deviceHasKey(it.id, action.keyCode) + } + + val device = deviceThatHasKey + ?: devicesWithSameDescriptor.singleOrNull { it.name == action.device.name } + ?: devicesWithSameDescriptor[0] + + return device.id + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt index 7df39b3771..1523ebb6cb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/ShellCommandActionScreen.kt @@ -38,11 +38,7 @@ import androidx.compose.material3.Tab import androidx.compose.material3.Text import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf 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 @@ -54,7 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.getFullMessage import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperSegmentedButtonRow import io.github.sds100.keymapper.base.utils.ui.compose.SliderOptionText @@ -71,7 +67,9 @@ import kotlinx.coroutines.launch data class ShellCommandActionState( val description: String = "", + val descriptionError: String? = null, val command: String = "", + val commandError: String? = null, val executionMode: ShellExecutionMode = ShellExecutionMode.STANDARD, /** * UI works with seconds for user-friendliness @@ -79,7 +77,7 @@ data class ShellCommandActionState( val timeoutSeconds: Int = 10, val isRunning: Boolean = false, val testResult: KMResult? = null, - val proModeStatus: ProModeStatus = ProModeStatus.UNSUPPORTED, + val expertModeStatus: ExpertModeStatus = ExpertModeStatus.UNSUPPORTED, ) @Composable @@ -98,7 +96,7 @@ fun ShellCommandActionScreen( onKillClick = viewModel::onKillClick, onDoneClick = viewModel::onDoneClick, onCancelClick = viewModel::onCancelClick, - onSetupProModeClick = viewModel::onSetupProModeClick, + onSetupExpertModeClick = viewModel::onSetupExpertModeClick, ) } @@ -111,20 +109,21 @@ private fun ShellCommandActionScreen( onCommandChanged: (String) -> Unit = {}, onExecutionModeChanged: (ShellExecutionMode) -> Unit = {}, onTimeoutChanged: (Int) -> Unit = {}, - onTestClick: () -> Unit = {}, + /** + * Returns whether validation passed + */ + onTestClick: () -> Boolean = { true }, onKillClick: () -> Unit = {}, - onDoneClick: () -> Unit = {}, + /** + * Returns whether validation passed + */ + onDoneClick: () -> Boolean = { true }, onCancelClick: () -> Unit = {}, - onSetupProModeClick: () -> Unit = {}, + onSetupExpertModeClick: () -> Unit = {}, ) { val scrollState = rememberScrollState() val scope = rememberCoroutineScope() - var descriptionError: String? by rememberSaveable { mutableStateOf(null) } - var commandError: String? by rememberSaveable { mutableStateOf(null) } - val descriptionEmptyErrorString = stringResource(R.string.error_cant_be_empty) - val commandEmptyErrorString = stringResource(R.string.action_shell_command_command_empty_error) - Scaffold( modifier = modifier, topBar = { @@ -137,24 +136,11 @@ private fun ShellCommandActionScreen( floatingActionButton = { ExtendedFloatingActionButton( onClick = { - var hasError = false - - if (state.description.isBlank()) { - descriptionError = descriptionEmptyErrorString - hasError = true - } - - if (state.command.isBlank()) { - commandError = commandEmptyErrorString - hasError = true - } - - if (hasError) { + // Go to the configuration tab if validation failed + if (!onDoneClick()) { scope.launch { scrollState.animateScrollTo(0) } - } else { - onDoneClick() } }, text = { Text(stringResource(R.string.pos_done)) }, @@ -226,29 +212,20 @@ private fun ShellCommandActionScreen( 0 -> ShellCommandConfigurationContent( modifier = Modifier.fillMaxSize(), state = state, - descriptionError = descriptionError, - commandError = commandError, - onDescriptionChanged = { - descriptionError = null - onDescriptionChanged(it) - }, - onCommandChanged = { - commandError = null - onCommandChanged(it) - }, + descriptionError = state.descriptionError, + commandError = state.commandError, + onDescriptionChanged = onDescriptionChanged, + onCommandChanged = onCommandChanged, onExecutionModeChanged = onExecutionModeChanged, onTimeoutChanged = onTimeoutChanged, onTestClick = { - if (state.command.isBlank()) { - commandError = commandEmptyErrorString - } else { - onTestClick() + if (onTestClick()) { scope.launch { pagerState.animateScrollToPage(1) // Switch to output tab } } }, - onSetupProModeClick = onSetupProModeClick, + onSetupExpertModeClick = onSetupExpertModeClick, ) 1 -> ShellCommandOutputContent( @@ -273,7 +250,7 @@ private fun ShellCommandConfigurationContent( onExecutionModeChanged: (ShellExecutionMode) -> Unit, onTimeoutChanged: (Int) -> Unit, onTestClick: () -> Unit, - onSetupProModeClick: () -> Unit, + onSetupExpertModeClick: () -> Unit, ) { val keyboardController = LocalSoftwareKeyboardController.current Column( @@ -350,18 +327,18 @@ private fun ShellCommandConfigurationContent( ) if (state.executionMode == ShellExecutionMode.ADB && - state.proModeStatus != ProModeStatus.ENABLED + state.expertModeStatus != ExpertModeStatus.ENABLED ) { OutlinedButton( modifier = Modifier.fillMaxWidth(), - onClick = onSetupProModeClick, - enabled = state.proModeStatus != ProModeStatus.UNSUPPORTED, + onClick = onSetupExpertModeClick, + enabled = state.expertModeStatus != ExpertModeStatus.UNSUPPORTED, ) { Text( - if (state.proModeStatus == ProModeStatus.UNSUPPORTED) { - stringResource(R.string.action_shell_command_setup_pro_mode_unsupported) + if (state.expertModeStatus == ExpertModeStatus.UNSUPPORTED) { + stringResource(R.string.action_shell_command_setup_expert_mode_unsupported) } else { - stringResource(R.string.action_shell_command_setup_pro_mode) + stringResource(R.string.action_shell_command_setup_expert_mode) }, ) } @@ -378,7 +355,7 @@ private fun ShellCommandConfigurationContent( state.executionMode != ShellExecutionMode.ADB || ( state.executionMode == ShellExecutionMode.ADB && - state.proModeStatus == ProModeStatus.ENABLED + state.expertModeStatus == ExpertModeStatus.ENABLED ) ), ) { @@ -598,14 +575,14 @@ private fun PreviewShellCommandActionScreenTesting() { @Preview @Composable -private fun PreviewShellCommandActionScreenProModeUnsupported() { +private fun PreviewShellCommandActionScreenExpertModeUnsupported() { KeyMapperTheme { ShellCommandActionScreen( state = ShellCommandActionState( description = "ADB command example", command = "echo 'Hello from ADB'", executionMode = ShellExecutionMode.ADB, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, ), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt index b890e9ac19..1bca36c182 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionBottomSheet.kt @@ -16,6 +16,7 @@ import androidx.compose.foundation.text.InlineTextContent import androidx.compose.foundation.text.appendInlineContent import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.Keyboard import androidx.compose.material.icons.rounded.Remove @@ -44,15 +45,13 @@ import androidx.compose.ui.unit.dp 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.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.ui.compose.AccessibilityServiceRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText +import io.github.sds100.keymapper.base.utils.ui.compose.ExpertModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.HeaderText import io.github.sds100.keymapper.base.utils.ui.compose.InputMethodRequirementRow -import io.github.sds100.keymapper.base.utils.ui.compose.ProModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.filledTonalButtonColorsError -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @OptIn(ExperimentalMaterial3Api::class) @Composable @@ -62,9 +61,9 @@ fun FixKeyEventActionBottomSheet( sheetState: SheetState, onDismissRequest: () -> Unit = {}, onSelectInputMethod: () -> Unit = {}, - onSelectProMode: () -> Unit = {}, + onSelectExpertMode: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onEnableInputMethodClick: () -> Unit = {}, onChooseInputMethodClick: () -> Unit = {}, onDoneClick: () -> Unit = {}, @@ -135,18 +134,18 @@ fun FixKeyEventActionBottomSheet( ) } - val isProModeUnsupported = state.proModeStatus == ProModeStatus.UNSUPPORTED + val isExpertModeUnsupported = state.expertModeStatus == ExpertModeStatus.UNSUPPORTED FixKeyEventActionOptionCard( - onClick = onSelectProMode, - selected = state is FixKeyEventActionState.ProMode, - title = stringResource(R.string.pro_mode_app_bar_title), - icon = KeyMapperIcons.ProModeIcon, - enabled = !isProModeUnsupported, + onClick = onSelectExpertMode, + selected = state is FixKeyEventActionState.ExpertMode, + title = stringResource(R.string.expert_mode_app_bar_title), + icon = Icons.Outlined.OfflineBolt, + enabled = !isExpertModeUnsupported, ) { - if (isProModeUnsupported) { + if (isExpertModeUnsupported) { Text( - stringResource(R.string.trigger_setup_pro_mode_unsupported), + stringResource(R.string.trigger_setup_expert_mode_unsupported), style = MaterialTheme.typography.bodyMedium, color = MaterialTheme.colorScheme.error, ) @@ -154,11 +153,11 @@ fun FixKeyEventActionBottomSheet( val annotatedText = buildAnnotatedString { appendInlineContent("icon", "[icon]") append(" ") - append(stringResource(R.string.fix_key_event_action_pro_mode_text_1)) + append(stringResource(R.string.fix_key_event_action_expert_mode_text_1)) appendLine() appendInlineContent("icon", "[icon]") append(" ") - append(stringResource(R.string.fix_key_event_action_pro_mode_text_2)) + append(stringResource(R.string.fix_key_event_action_expert_mode_text_2)) } val inlineContent = mapOf( Pair( @@ -224,13 +223,13 @@ fun FixKeyEventActionBottomSheet( ) } - is FixKeyEventActionState.ProMode -> { - ProModeRequirementRow( + is FixKeyEventActionState.ExpertMode -> { + ExpertModeRequirementRow( modifier = Modifier.fillMaxWidth(), isVisible = true, - proModeStatus = state.proModeStatus, + expertModeStatus = state.expertModeStatus, buttonColors = ButtonDefaults.filledTonalButtonColorsError(), - onClick = onEnableProModeClick, + onClick = onEnableExpertModeClick, ) } } @@ -317,7 +316,7 @@ private fun InputMethodPreview() { enablingRequiresUserInput = true, isAccessibilityServiceEnabled = true, isAutoSwitchImeEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, ), ) } @@ -326,7 +325,7 @@ private fun InputMethodPreview() { @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun ProModePreview() { +private fun ExpertModePreview() { KeyMapperTheme { val sheetState = SheetState( skipPartiallyExpanded = true, @@ -336,8 +335,8 @@ private fun ProModePreview() { FixKeyEventActionBottomSheet( sheetState = sheetState, - state = FixKeyEventActionState.ProMode( - proModeStatus = ProModeStatus.DISABLED, + state = FixKeyEventActionState.ExpertMode( + expertModeStatus = ExpertModeStatus.DISABLED, isAccessibilityServiceEnabled = true, ), ) @@ -347,7 +346,7 @@ private fun ProModePreview() { @OptIn(ExperimentalMaterial3Api::class) @Preview @Composable -private fun ProModeUnsupportedPreview() { +private fun ExpertModeUnsupportedPreview() { KeyMapperTheme { val sheetState = SheetState( skipPartiallyExpanded = true, @@ -358,7 +357,7 @@ private fun ProModeUnsupportedPreview() { FixKeyEventActionBottomSheet( sheetState = sheetState, state = FixKeyEventActionState.InputMethod( - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, isEnabled = false, isChosen = false, enablingRequiresUserInput = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt index 48a8a16c5c..61f8cbe818 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionDelegate.kt @@ -5,7 +5,7 @@ import dagger.hilt.android.scopes.ViewModelScoped import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate import io.github.sds100.keymapper.base.system.accessibility.ControlAccessibilityServiceUseCase import io.github.sds100.keymapper.base.trigger.SetupInputMethodUseCase -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus 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 @@ -52,19 +52,19 @@ class FixKeyEventActionDelegateImpl @Inject constructor( DialogProvider by dialogProvider, NavigationProvider by navigationProvider { - private val proModeStatus: Flow = + private val expertModeStatus: Flow = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeConnectionManager.connectionState.map { state -> when (state) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } } } else { - flowOf(ProModeStatus.UNSUPPORTED) + flowOf(ExpertModeStatus.UNSUPPORTED) } - private val isProModeSelected: Flow = + private val isExpertModeSelected: Flow = preferenceRepository.get(Keys.keyEventActionsUseSystemBridge) .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } @@ -82,26 +82,26 @@ class FixKeyEventActionDelegateImpl @Inject constructor( @OptIn(ExperimentalCoroutinesApi::class) private fun buildStateFlow(): Flow { - return isProModeSelected.flatMapLatest { isProModeSelected -> - if (isProModeSelected) { + return isExpertModeSelected.flatMapLatest { isExpertModeSelected -> + if (isExpertModeSelected) { combine( - proModeStatus, + expertModeStatus, controlAccessibilityServiceUseCase.serviceState, - ) { proModeStatus, serviceState -> - FixKeyEventActionState.ProMode( - proModeStatus = proModeStatus, + ) { expertModeStatus, serviceState -> + FixKeyEventActionState.ExpertMode( + expertModeStatus = expertModeStatus, isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, ) } } else { combine( - proModeStatus, + expertModeStatus, setupInputMethodUseCase.isEnabled, setupInputMethodUseCase.isChosen, controlAccessibilityServiceUseCase.serviceState, preferenceRepository.get(Keys.changeImeOnInputFocus), - ) { proModeStatus, isEnabled, isChosen, serviceState, changeImeOnInputFocus -> + ) { expertModeStatus, isEnabled, isChosen, serviceState, changeImeOnInputFocus -> val enablingRequiresUserInput = Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU @@ -111,7 +111,7 @@ class FixKeyEventActionDelegateImpl @Inject constructor( enablingRequiresUserInput = enablingRequiresUserInput, isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, isAutoSwitchImeEnabled = changeImeOnInputFocus ?: PreferenceDefaults.CHANGE_IME_ON_INPUT_FOCUS, ) @@ -134,9 +134,9 @@ class FixKeyEventActionDelegateImpl @Inject constructor( } } - override fun onEnableProModeForKeyEventActionsClick() { + override fun onEnableExpertModeForKeyEventActionsClick() { viewModelScope.launch { - navigate("fix_key_event_action_pro_mode", NavDestination.ProMode) + navigate("fix_key_event_action_expert_mode", NavDestination.ExpertMode) } } @@ -154,7 +154,7 @@ class FixKeyEventActionDelegateImpl @Inject constructor( } } - override fun onSelectProMode() { + override fun onSelectExpertMode() { preferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) } @@ -175,10 +175,10 @@ interface FixKeyEventActionDelegate { fun showFixKeyEventActionBottomSheet() fun dismissFixKeyEventActionBottomSheet() fun onEnableAccessibilityServiceClick() - fun onEnableProModeForKeyEventActionsClick() + fun onEnableExpertModeForKeyEventActionsClick() fun onEnableImeClick() fun onChooseImeClick() - fun onSelectProMode() + fun onSelectExpertMode() fun onSelectInputMethod() fun onAutoSwitchImeCheckedChange(checked: Boolean) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt index 5bacfc2db5..370dd54e1a 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/actions/keyevent/FixKeyEventActionState.kt @@ -1,10 +1,10 @@ package io.github.sds100.keymapper.base.actions.keyevent -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus sealed class FixKeyEventActionState { abstract val isAccessibilityServiceEnabled: Boolean - abstract val proModeStatus: ProModeStatus + abstract val expertModeStatus: ExpertModeStatus data class InputMethod( val isEnabled: Boolean, @@ -16,11 +16,11 @@ sealed class FixKeyEventActionState { val enablingRequiresUserInput: Boolean, val isAutoSwitchImeEnabled: Boolean, override val isAccessibilityServiceEnabled: Boolean, - override val proModeStatus: ProModeStatus, + override val expertModeStatus: ExpertModeStatus, ) : FixKeyEventActionState() - data class ProMode( + data class ExpertMode( override val isAccessibilityServiceEnabled: Boolean, - override val proModeStatus: ProModeStatus, + override val expertModeStatus: ExpertModeStatus, ) : FixKeyEventActionState() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/compose/ComposeTheme.kt b/base/src/main/java/io/github/sds100/keymapper/base/compose/ComposeTheme.kt index 23ca891235..ddf61c8344 100755 --- a/base/src/main/java/io/github/sds100/keymapper/base/compose/ComposeTheme.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/compose/ComposeTheme.kt @@ -103,6 +103,12 @@ fun KeyMapperTheme(darkTheme: Boolean = isSystemInDarkTheme(), content: @Composa val customColorsPalette = if (darkTheme) ComposeCustomColors.DarkPalette else ComposeCustomColors.LightPalette + SetSystemChrome( + statusBarColor = colorScheme.surfaceContainer, + navigationBarColor = colorScheme.surfaceContainer, + isDarkTheme = darkTheme, + ) + CompositionLocalProvider( LocalCustomColorsPalette provides customColorsPalette, ) { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/compose/SystemChrome.kt b/base/src/main/java/io/github/sds100/keymapper/base/compose/SystemChrome.kt new file mode 100644 index 0000000000..59e8901869 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/compose/SystemChrome.kt @@ -0,0 +1,47 @@ +package io.github.sds100.keymapper.base.compose + +import android.content.Context +import android.content.ContextWrapper +import androidx.activity.ComponentActivity +import androidx.activity.SystemBarStyle +import androidx.activity.enableEdgeToEdge +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.LocalView + +@Composable +fun SetSystemChrome(statusBarColor: Color, navigationBarColor: Color, isDarkTheme: Boolean) { + val context = LocalContext.current + val view = LocalView.current + + if (!view.isInEditMode) { + // Use keys to ensure it updates when theme changes. + // LaunchedEffect runs when keys change, and on initial composition. + // It also runs when restoring back stack if the composable is recreated. + LaunchedEffect(isDarkTheme, statusBarColor, navigationBarColor) { + val activity = context.findActivity() ?: return@LaunchedEffect + + activity.enableEdgeToEdge( + statusBarStyle = if (isDarkTheme) { + SystemBarStyle.dark(statusBarColor.toArgb()) + } else { + SystemBarStyle.light(statusBarColor.toArgb(), statusBarColor.toArgb()) + }, + navigationBarStyle = if (isDarkTheme) { + SystemBarStyle.dark(navigationBarColor.toArgb()) + } else { + SystemBarStyle.light(navigationBarColor.toArgb(), navigationBarColor.toArgb()) + }, + ) + } + } +} + +private tailrec fun Context.findActivity(): ComponentActivity? = when (this) { + is ComponentActivity -> this + is ContextWrapper -> baseContext.findActivity() + else -> null +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt index d3a956cd9c..857f30330c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintScreen.kt @@ -2,7 +2,6 @@ package io.github.sds100.keymapper.base.constraints 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.calculateEndPadding import androidx.compose.foundation.layout.calculateStartPadding @@ -15,25 +14,19 @@ import androidx.compose.foundation.lazy.grid.GridCells import androidx.compose.foundation.lazy.grid.LazyVerticalGrid import androidx.compose.foundation.lazy.grid.items import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.automirrored.rounded.ArrowBack import androidx.compose.material.icons.rounded.Android -import androidx.compose.material.icons.rounded.Search +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.DockedSearchBar import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Scaffold -import androidx.compose.material3.SearchBarDefaults import androidx.compose.material3.Surface import androidx.compose.material3.Text +import androidx.compose.material3.TopAppBar import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -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.LocalLayoutDirection @@ -48,25 +41,28 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle 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 ChooseConstraintScreen(modifier: Modifier = Modifier, viewModel: ChooseConstraintViewModel) { - val listItems by viewModel.listItems.collectAsStateWithLifecycle() + val state by viewModel.groups.collectAsStateWithLifecycle() val query by viewModel.searchQuery.collectAsStateWithLifecycle() TimeConstraintBottomSheet(viewModel) ChooseConstraintScreen( modifier = modifier, - state = listItems, + state = state, query = query, onQueryChange = { newQuery -> viewModel.searchQuery.update { newQuery } }, onCloseSearch = { viewModel.searchQuery.update { null } }, - onClickAction = viewModel::onListItemClick, + onClickConstraint = viewModel::onListItemClick, onNavigateBack = viewModel::onNavigateBack, ) } @@ -75,78 +71,30 @@ fun ChooseConstraintScreen(modifier: Modifier = Modifier, viewModel: ChooseConst @Composable private fun ChooseConstraintScreen( modifier: Modifier = Modifier, - state: State>, + state: State>, query: String? = null, onQueryChange: (String) -> Unit = {}, onCloseSearch: () -> Unit = {}, - onClickAction: (String) -> Unit = {}, + onClickConstraint: (String) -> Unit = {}, onNavigateBack: () -> Unit = {}, ) { - var isExpanded: Boolean by rememberSaveable { mutableStateOf(false) } - Scaffold( modifier = modifier.displayCutoutPadding(), + topBar = { + TopAppBar( + title = { Text(stringResource(R.string.choose_constraint_title)) }, + ) + }, bottomBar = { BottomAppBar( modifier = Modifier.imePadding(), actions = { - IconButton(onClick = { - if (isExpanded) { - onCloseSearch() - isExpanded = false - } else { - onNavigateBack() - } - }) { - Icon( - Icons.AutoMirrored.Rounded.ArrowBack, - contentDescription = stringResource( - R.string.bottom_app_bar_back_content_description, - ), - ) - } - - DockedSearchBar( - modifier = Modifier.align(Alignment.CenterVertically), - inputField = { - SearchBarDefaults.InputField( - modifier = Modifier.align(Alignment.CenterVertically), - onSearch = { - onQueryChange(it) - isExpanded = false - }, - leadingIcon = { - Icon( - Icons.Rounded.Search, - contentDescription = null, - ) - }, - enabled = state is State.Data, - placeholder = { Text(stringResource(R.string.search_placeholder)) }, - query = query ?: "", - onQueryChange = onQueryChange, - expanded = isExpanded, - onExpandedChange = { expanded -> - if (expanded) { - isExpanded = true - } else { - onCloseSearch() - isExpanded = false - } - }, - ) - }, - // This is false to prevent an empty "content" showing underneath. - expanded = isExpanded, - onExpandedChange = { expanded -> - if (expanded) { - isExpanded = true - } else { - onCloseSearch() - isExpanded = false - } - }, - content = {}, + SearchAppBarActions( + onCloseSearch = onCloseSearch, + onNavigateBack = onNavigateBack, + onQueryChange = onQueryChange, + enabled = state is State.Data, + query = query, ) }, ) @@ -167,33 +115,20 @@ private fun ChooseConstraintScreen( ), ) { - Column { - Text( - modifier = Modifier.padding( - start = 16.dp, - end = 16.dp, - top = 16.dp, - bottom = 8.dp, - ), - text = stringResource(R.string.choose_constraint_title), - style = MaterialTheme.typography.titleLarge, - ) - - when (state) { - State.Loading -> LoadingScreen(modifier = Modifier.fillMaxSize()) + when (state) { + State.Loading -> LoadingScreen(modifier = Modifier.fillMaxSize()) - is State.Data -> { - if (state.data.isEmpty()) { - EmptyScreen( - modifier = Modifier.fillMaxSize(), - ) - } else { - ListScreen( - modifier = Modifier.fillMaxSize(), - listItems = state.data, - onClickAction = onClickAction, - ) - } + is State.Data -> { + if (state.data.isEmpty()) { + EmptyScreen( + modifier = Modifier.fillMaxSize(), + ) + } else { + ListScreen( + modifier = Modifier.fillMaxSize(), + groups = state.data, + onClickConstraint = onClickConstraint, + ) } } } @@ -233,8 +168,8 @@ private fun EmptyScreen(modifier: Modifier = Modifier) { @Composable private fun ListScreen( modifier: Modifier = Modifier, - listItems: List, - onClickAction: (String) -> Unit, + groups: List, + onClickConstraint: (String) -> Unit, ) { LazyVerticalGrid( modifier = modifier, @@ -243,12 +178,21 @@ private fun ListScreen( verticalArrangement = Arrangement.spacedBy(8.dp), horizontalArrangement = Arrangement.spacedBy(8.dp), ) { - items(listItems, key = { it.id }) { model -> - SimpleListItemFixedHeight( - modifier = Modifier.fillMaxWidth(), - model = model, - onClick = { onClickAction(model.id) }, - ) + for (group in groups) { + stickyHeader(contentType = "header") { + SimpleListItemHeader(modifier = Modifier.fillMaxWidth(), text = group.header) + } + + items( + group.items, + contentType = { "list_item" }, + ) { model -> + SimpleListItemFixedHeight( + modifier = Modifier.fillMaxWidth(), + model = model, + onClick = { onClickConstraint(model.id) }, + ) + } } } } @@ -261,18 +205,30 @@ private fun PreviewList() { query = "Search query", state = State.Data( listOf( - SimpleListItemModel( - "app", - title = "App in foreground", - icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + SimpleListItemGroup( + header = "Apps", + items = listOf( + SimpleListItemModel( + "app1", + title = "App in foreground", + icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + ), + SimpleListItemModel( + "app2", + title = "App not in foreground", + icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + ), + ), ), - SimpleListItemModel( - "app", - title = "App not in foreground", - icon = ComposeIconInfo.Vector(Icons.Rounded.Android), - subtitle = "Error", - isSubtitleError = true, - isEnabled = false, + SimpleListItemGroup( + header = "Bluetooth", + items = listOf( + SimpleListItemModel( + "bt1", + title = "Bluetooth device connected", + icon = ComposeIconInfo.Vector(Icons.Rounded.Bluetooth), + ), + ), ), ), ), @@ -285,21 +241,42 @@ private fun PreviewList() { private fun PreviewGrid() { KeyMapperTheme { ChooseConstraintScreen( - query = "Search query", state = State.Data( listOf( - SimpleListItemModel( - "app1", - title = "App in foreground", - icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + SimpleListItemGroup( + header = "Apps", + items = listOf( + SimpleListItemModel( + "app1", + title = "App in foreground", + icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + ), + SimpleListItemModel( + "app2", + title = "App not in foreground", + icon = ComposeIconInfo.Vector(Icons.Rounded.Android), + ), + ), ), - SimpleListItemModel( - "app2", - title = "App not in foreground", - icon = ComposeIconInfo.Vector(Icons.Rounded.Android), - subtitle = "Error", - isSubtitleError = true, - isEnabled = false, + SimpleListItemGroup( + header = "WiFi", + items = listOf( + SimpleListItemModel( + "wifi1", + title = "WiFi is on", + icon = ComposeIconInfo.Vector(Icons.Rounded.Wifi), + subtitle = "Requires root", + isSubtitleError = true, + ), + SimpleListItemModel( + "wifi2", + title = "WiFi is off", + icon = ComposeIconInfo.Vector(Icons.Rounded.Wifi), + subtitle = "Requires root", + isSubtitleError = true, + isEnabled = false, + ), + ), ), ), ), diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt index c39dacae37..836834e723 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ChooseConstraintViewModel.kt @@ -1,5 +1,7 @@ package io.github.sds100.keymapper.base.constraints +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.StayCurrentPortrait import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue @@ -15,9 +17,12 @@ 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.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.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.system.camera.CameraLens import javax.inject.Inject @@ -45,70 +50,46 @@ class ChooseConstraintViewModel @Inject constructor( NavigationProvider by navigationProvider { companion object { - private val ALL_CONSTRAINTS_ORDERED: Array = arrayOf( - ConstraintId.APP_IN_FOREGROUND, - ConstraintId.APP_NOT_IN_FOREGROUND, - ConstraintId.APP_PLAYING_MEDIA, - ConstraintId.APP_NOT_PLAYING_MEDIA, - ConstraintId.MEDIA_PLAYING, - ConstraintId.MEDIA_NOT_PLAYING, - - ConstraintId.BT_DEVICE_CONNECTED, - ConstraintId.BT_DEVICE_DISCONNECTED, - - ConstraintId.SCREEN_ON, - ConstraintId.SCREEN_OFF, - - ConstraintId.ORIENTATION_PORTRAIT, - ConstraintId.ORIENTATION_LANDSCAPE, - ConstraintId.ORIENTATION_0, - ConstraintId.ORIENTATION_90, - ConstraintId.ORIENTATION_180, - ConstraintId.ORIENTATION_270, - - ConstraintId.FLASHLIGHT_ON, - ConstraintId.FLASHLIGHT_OFF, - - ConstraintId.WIFI_ON, - ConstraintId.WIFI_OFF, - ConstraintId.WIFI_CONNECTED, - ConstraintId.WIFI_DISCONNECTED, - - ConstraintId.IME_CHOSEN, - ConstraintId.IME_NOT_CHOSEN, - - ConstraintId.KEYBOARD_SHOWING, - ConstraintId.KEYBOARD_NOT_SHOWING, - - ConstraintId.DEVICE_IS_LOCKED, - ConstraintId.DEVICE_IS_UNLOCKED, - ConstraintId.LOCK_SCREEN_SHOWING, - ConstraintId.LOCK_SCREEN_NOT_SHOWING, - - ConstraintId.IN_PHONE_CALL, - ConstraintId.NOT_IN_PHONE_CALL, - ConstraintId.PHONE_RINGING, - - ConstraintId.CHARGING, - ConstraintId.DISCHARGING, - - ConstraintId.HINGE_CLOSED, - ConstraintId.HINGE_OPEN, - - ConstraintId.TIME, + // Synthetic IDs for consolidated orientation list items (not actual ConstraintIds) + private const val DISPLAY_ORIENTATION_LIST_ITEM_ID = "display_orientation" + private const val PHYSICAL_ORIENTATION_LIST_ITEM_ID = "physical_orientation" + + private val CATEGORY_ORDER = arrayOf( + ConstraintCategory.APPS, + ConstraintCategory.MEDIA, + ConstraintCategory.BLUETOOTH, + ConstraintCategory.DISPLAY, + ConstraintCategory.FLASHLIGHT, + ConstraintCategory.WIFI, + ConstraintCategory.KEYBOARD, + ConstraintCategory.LOCK, + ConstraintCategory.PHONE, + ConstraintCategory.POWER, + ConstraintCategory.DEVICE, + ConstraintCategory.TIME, ) } private val returnResult = MutableSharedFlow() - private val allListItems: List by lazy { buildListItems() } + private val allGroupedListItems: List by lazy { buildListGroups() } val searchQuery = MutableStateFlow(null) - val listItems: StateFlow>> = + val groups: StateFlow>> = searchQuery.map { query -> - val filteredItems = allListItems.filter { it.title.containsQuery(query) } - State.Data(filteredItems) + val groups = allGroupedListItems.mapNotNull { group -> + + val filteredItems = group.items.filter { it.title.containsQuery(query) } + + if (filteredItems.isEmpty()) { + return@mapNotNull null + } else { + group.copy(items = filteredItems) + } + } + + State.Data(groups) }.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) var timeConstraintState: ConstraintData.Time? by mutableStateOf(null) @@ -138,6 +119,18 @@ class ChooseConstraintViewModel @Inject constructor( fun onListItemClick(id: String) { viewModelScope.launch { + // Handle synthetic list item IDs for consolidated orientation constraints + when (id) { + DISPLAY_ORIENTATION_LIST_ITEM_ID -> { + onSelectDisplayOrientationConstraint() + return@launch + } + PHYSICAL_ORIENTATION_LIST_ITEM_ID -> { + onSelectPhysicalOrientationConstraint() + return@launch + } + } + when (val constraintType = ConstraintId.valueOf(id)) { ConstraintId.APP_IN_FOREGROUND, ConstraintId.APP_NOT_IN_FOREGROUND, @@ -158,32 +151,60 @@ class ChooseConstraintViewModel @Inject constructor( ConstraintId.SCREEN_OFF -> returnResult.emit(ConstraintData.ScreenOff) - ConstraintId.ORIENTATION_PORTRAIT -> + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> returnResult.emit(ConstraintData.OrientationPortrait) - ConstraintId.ORIENTATION_LANDSCAPE -> + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> returnResult.emit(ConstraintData.OrientationLandscape) - ConstraintId.ORIENTATION_0 -> + ConstraintId.DISPLAY_ORIENTATION_0 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0), ) - ConstraintId.ORIENTATION_90 -> + ConstraintId.DISPLAY_ORIENTATION_90 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90), ) - ConstraintId.ORIENTATION_180 -> + ConstraintId.DISPLAY_ORIENTATION_180 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180), ) - ConstraintId.ORIENTATION_270 -> + ConstraintId.DISPLAY_ORIENTATION_270 -> returnResult.emit( ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270), ) + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> + returnResult.emit( + ConstraintData.PhysicalOrientation( + physicalOrientation = PhysicalOrientation.PORTRAIT, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> + returnResult.emit( + ConstraintData.PhysicalOrientation( + physicalOrientation = PhysicalOrientation.LANDSCAPE, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + returnResult.emit( + ConstraintData.PhysicalOrientation( + physicalOrientation = PhysicalOrientation.PORTRAIT_INVERTED, + ), + ) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + returnResult.emit( + ConstraintData.PhysicalOrientation( + physicalOrientation = PhysicalOrientation.LANDSCAPE_INVERTED, + ), + ) + ConstraintId.FLASHLIGHT_ON -> { val lens = chooseFlashlightLens() ?: return@launch returnResult.emit(ConstraintData.FlashlightOn(lens = lens)) @@ -278,25 +299,154 @@ class ChooseConstraintViewModel @Inject constructor( return cameraLens } - private fun buildListItems(): List = buildList { - ALL_CONSTRAINTS_ORDERED.forEach { id -> - val title = getString(ConstraintUtils.getTitleStringId(id)) - val icon = ConstraintUtils.getIcon(id) - val error = useCase.isSupported(id) - - val listItem = SimpleListItemModel( - id = id.toString(), - title = title, - icon = icon, - subtitle = error?.getFullMessage(this@ChooseConstraintViewModel), - isSubtitleError = true, - isEnabled = error == null, + private suspend fun onSelectDisplayOrientationConstraint() { + val items = listOf( + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT to + getString(R.string.constraint_choose_orientation_portrait), + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE to + getString(R.string.constraint_choose_orientation_landscape), + ConstraintId.DISPLAY_ORIENTATION_0 to + getString(R.string.constraint_choose_orientation_0), + ConstraintId.DISPLAY_ORIENTATION_90 to + getString(R.string.constraint_choose_orientation_90), + ConstraintId.DISPLAY_ORIENTATION_180 to + getString(R.string.constraint_choose_orientation_180), + ConstraintId.DISPLAY_ORIENTATION_270 to + getString(R.string.constraint_choose_orientation_270), + ) + + val dialog = DialogModel.SingleChoice(items) + val selectedOrientation = showDialog("choose_display_orientation", dialog) ?: return + + val constraintData = when (selectedOrientation) { + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintId.DISPLAY_ORIENTATION_0 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_0) + ConstraintId.DISPLAY_ORIENTATION_90 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_90) + ConstraintId.DISPLAY_ORIENTATION_180 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_180) + ConstraintId.DISPLAY_ORIENTATION_270 -> + ConstraintData.OrientationCustom(orientation = Orientation.ORIENTATION_270) + else -> return + } + + returnResult.emit(constraintData) + } + + private suspend fun onSelectPhysicalOrientationConstraint() { + val items = listOf( + PhysicalOrientation.PORTRAIT to + getString(R.string.constraint_choose_physical_orientation_portrait), + PhysicalOrientation.LANDSCAPE to + getString(R.string.constraint_choose_physical_orientation_landscape), + PhysicalOrientation.PORTRAIT_INVERTED to + getString(R.string.constraint_choose_physical_orientation_portrait_inverted), + PhysicalOrientation.LANDSCAPE_INVERTED to + getString(R.string.constraint_choose_physical_orientation_landscape_inverted), + ) + + val dialog = DialogModel.SingleChoice(items) + val selectedOrientation = showDialog("choose_physical_orientation", dialog) ?: return + + returnResult.emit( + ConstraintData.PhysicalOrientation(physicalOrientation = selectedOrientation), + ) + } + + private fun buildListGroups(): List = buildList { + // Filter out individual orientation constraints - show only the consolidated ones + val filteredConstraints = ConstraintId.entries.filter { constraintId -> + constraintId !in listOf( + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT, + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_270, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, ) + } - add(listItem) + val listItems = buildListItems(filteredConstraints) + + // Add synthetic orientation list items + val displayOrientationItem = SimpleListItemModel( + id = DISPLAY_ORIENTATION_LIST_ITEM_ID, + title = getString(R.string.constraint_choose_screen_orientation), + icon = ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait), + isEnabled = true, + ) + + val physicalOrientationItem = SimpleListItemModel( + id = PHYSICAL_ORIENTATION_LIST_ITEM_ID, + title = getString(R.string.constraint_choose_physical_orientation), + icon = ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait), + isEnabled = true, + ) + + for (category in CATEGORY_ORDER) { + val header = getString(ConstraintUtils.getCategoryLabel(category)) + + val categoryItems = listItems.filter { item -> + item.isEnabled && + try { + ConstraintUtils.getCategory(ConstraintId.valueOf(item.id)) == category + } catch (e: IllegalArgumentException) { + false + } + }.toMutableList() + + // Add synthetic orientation items to DISPLAY category + if (category == ConstraintCategory.DISPLAY) { + categoryItems.add(displayOrientationItem) + categoryItems.add(physicalOrientationItem) + } + + val group = SimpleListItemGroup( + header, + items = categoryItems, + ) + + if (group.items.isNotEmpty()) { + add(group) + } + } + + val unsupportedItems = listItems.filter { !it.isEnabled } + if (unsupportedItems.isNotEmpty()) { + val unsupportedGroup = SimpleListItemGroup( + header = getString(R.string.choose_constraint_group_unsupported), + items = unsupportedItems, + ) + add(unsupportedGroup) } } + private fun buildListItems(constraintIds: List): List = + buildList { + for (constraintId in constraintIds) { + val title = getString(ConstraintUtils.getTitleStringId(constraintId)) + val icon = ConstraintUtils.getIcon(constraintId) + val error = useCase.isSupported(constraintId) + + val listItem = SimpleListItemModel( + id = constraintId.toString(), + title = title, + icon = icon, + subtitle = error?.getFullMessage(this@ChooseConstraintViewModel), + isSubtitleError = true, + isEnabled = error == null, + ) + + add(listItem) + } + } + private suspend fun onSelectWifiConnectedConstraint(type: ConstraintId) { val knownSSIDs: List = useCase.getKnownWiFiSSIDs() diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt index 547802579f..a2aa471fbd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/Constraint.kt @@ -1,6 +1,7 @@ package io.github.sds100.keymapper.base.constraints import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.getKey import io.github.sds100.keymapper.common.utils.valueOrNull import io.github.sds100.keymapper.data.entities.ConstraintEntity @@ -69,21 +70,37 @@ sealed class ConstraintData { @Serializable data object OrientationPortrait : ConstraintData() { - override val id: ConstraintId = ConstraintId.ORIENTATION_PORTRAIT + override val id: ConstraintId = ConstraintId.DISPLAY_ORIENTATION_PORTRAIT } @Serializable data object OrientationLandscape : ConstraintData() { - override val id: ConstraintId = ConstraintId.ORIENTATION_LANDSCAPE + override val id: ConstraintId = ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE } @Serializable data class OrientationCustom(val orientation: Orientation) : ConstraintData() { override val id: ConstraintId = when (orientation) { - Orientation.ORIENTATION_0 -> ConstraintId.ORIENTATION_0 - Orientation.ORIENTATION_90 -> ConstraintId.ORIENTATION_90 - Orientation.ORIENTATION_180 -> ConstraintId.ORIENTATION_180 - Orientation.ORIENTATION_270 -> ConstraintId.ORIENTATION_270 + Orientation.ORIENTATION_0 -> ConstraintId.DISPLAY_ORIENTATION_0 + Orientation.ORIENTATION_90 -> ConstraintId.DISPLAY_ORIENTATION_90 + Orientation.ORIENTATION_180 -> ConstraintId.DISPLAY_ORIENTATION_180 + Orientation.ORIENTATION_270 -> ConstraintId.DISPLAY_ORIENTATION_270 + } + } + + @Serializable + data class PhysicalOrientation( + val physicalOrientation: io.github.sds100.keymapper.common.utils.PhysicalOrientation, + ) : ConstraintData() { + override val id: ConstraintId = when (physicalOrientation) { + io.github.sds100.keymapper.common.utils.PhysicalOrientation.PORTRAIT -> + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT + io.github.sds100.keymapper.common.utils.PhysicalOrientation.LANDSCAPE -> + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE + io.github.sds100.keymapper.common.utils.PhysicalOrientation.PORTRAIT_INVERTED -> + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED + io.github.sds100.keymapper.common.utils.PhysicalOrientation.LANDSCAPE_INVERTED -> + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED } } @@ -316,6 +333,15 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_PORTRAIT -> ConstraintData.OrientationPortrait ConstraintEntity.ORIENTATION_LANDSCAPE -> ConstraintData.OrientationLandscape + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT -> + ConstraintData.PhysicalOrientation(PhysicalOrientation.PORTRAIT) + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE -> + ConstraintData.PhysicalOrientation(PhysicalOrientation.LANDSCAPE) + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + ConstraintData.PhysicalOrientation(PhysicalOrientation.PORTRAIT_INVERTED) + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + ConstraintData.PhysicalOrientation(PhysicalOrientation.LANDSCAPE_INVERTED) + ConstraintEntity.SCREEN_OFF -> ConstraintData.ScreenOff ConstraintEntity.SCREEN_ON -> ConstraintData.ScreenOn @@ -499,6 +525,25 @@ object ConstraintEntityMapper { ConstraintEntity.ORIENTATION_PORTRAIT, ) + is ConstraintData.PhysicalOrientation -> when (constraint.data.physicalOrientation) { + PhysicalOrientation.PORTRAIT -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT, + ) + PhysicalOrientation.LANDSCAPE -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE, + ) + PhysicalOrientation.PORTRAIT_INVERTED -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ) + PhysicalOrientation.LANDSCAPE_INVERTED -> ConstraintEntity( + uid = constraint.uid, + ConstraintEntity.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + ) + } + is ConstraintData.ScreenOff -> ConstraintEntity( uid = constraint.uid, ConstraintEntity.SCREEN_OFF, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintCategory.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintCategory.kt new file mode 100644 index 0000000000..e4937d1529 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintCategory.kt @@ -0,0 +1,16 @@ +package io.github.sds100.keymapper.base.constraints + +enum class ConstraintCategory { + APPS, + MEDIA, + BLUETOOTH, + DISPLAY, + FLASHLIGHT, + WIFI, + KEYBOARD, + LOCK, + PHONE, + POWER, + DEVICE, + TIME, +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt index e8b3f784e9..4bbbcf1721 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintDependency.kt @@ -7,6 +7,7 @@ enum class ConstraintDependency { CONNECTED_BT_DEVICES, SCREEN_STATE, DISPLAY_ORIENTATION, + PHYSICAL_ORIENTATION, FLASHLIGHT_STATE, WIFI_SSID, WIFI_STATE, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt index b48b61b8c2..06463c6fd7 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintId.kt @@ -19,12 +19,17 @@ enum class ConstraintId { SCREEN_ON, SCREEN_OFF, - ORIENTATION_PORTRAIT, - ORIENTATION_LANDSCAPE, - ORIENTATION_0, - ORIENTATION_90, - ORIENTATION_180, - ORIENTATION_270, + DISPLAY_ORIENTATION_PORTRAIT, + DISPLAY_ORIENTATION_LANDSCAPE, + DISPLAY_ORIENTATION_0, + DISPLAY_ORIENTATION_90, + DISPLAY_ORIENTATION_180, + DISPLAY_ORIENTATION_270, + + PHYSICAL_ORIENTATION_PORTRAIT, + PHYSICAL_ORIENTATION_LANDSCAPE, + PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, FLASHLIGHT_ON, FLASHLIGHT_OFF, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt index 629a610c3a..00ac3781ee 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintSnapshot.kt @@ -4,6 +4,7 @@ import android.media.AudioManager import android.os.Build import io.github.sds100.keymapper.base.system.accessibility.IAccessibilityService import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.firstBlocking import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraAdapter @@ -43,6 +44,9 @@ class LazyConstraintSnapshot( devicesAdapter.connectedBluetoothDevices.value } private val orientation: Orientation by lazy { displayAdapter.cachedOrientation } + private val physicalOrientation: PhysicalOrientation by lazy { + displayAdapter.cachedPhysicalOrientation + } private val isScreenOn: Boolean by lazy { displayAdapter.isScreenOn.firstBlocking() } private val appsPlayingMedia: List by lazy { mediaAdapter.getActiveMediaSessionPackages() @@ -117,6 +121,9 @@ class LazyConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 + is ConstraintData.PhysicalOrientation -> + physicalOrientation == constraint.data.physicalOrientation + is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn is ConstraintData.FlashlightOff -> !cameraAdapter.isFlashlightOn(constraint.data.lens) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt index 62246d6817..94b02aebc9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUiHelper.kt @@ -6,6 +6,7 @@ import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.utils.ui.ResourceProvider 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.PhysicalOrientation import io.github.sds100.keymapper.common.utils.TimeUtils import io.github.sds100.keymapper.common.utils.handle import io.github.sds100.keymapper.common.utils.valueIfFailure @@ -82,6 +83,21 @@ class ConstraintUiHelper( is ConstraintData.OrientationPortrait -> getString(R.string.constraint_choose_orientation_portrait) + is ConstraintData.PhysicalOrientation -> { + val resId = when (constraint.data.physicalOrientation) { + PhysicalOrientation.PORTRAIT -> + R.string.constraint_choose_physical_orientation_portrait + PhysicalOrientation.LANDSCAPE -> + R.string.constraint_choose_physical_orientation_landscape + PhysicalOrientation.PORTRAIT_INVERTED -> + R.string.constraint_choose_physical_orientation_portrait_inverted + PhysicalOrientation.LANDSCAPE_INVERTED -> + R.string.constraint_choose_physical_orientation_landscape_inverted + } + + getString(resId) + } + is ConstraintData.ScreenOff -> getString(R.string.constraint_screen_off_description) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt index 2d7b1912aa..80cd56c1c7 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/ConstraintUtils.kt @@ -1,5 +1,6 @@ package io.github.sds100.keymapper.base.constraints +import androidx.annotation.StringRes import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Battery2Bar import androidx.compose.material.icons.outlined.BatteryChargingFull @@ -30,6 +31,89 @@ import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo object ConstraintUtils { + @StringRes + fun getCategoryLabel(category: ConstraintCategory): Int = when (category) { + ConstraintCategory.APPS -> R.string.constraint_cat_apps + ConstraintCategory.MEDIA -> R.string.constraint_cat_media + ConstraintCategory.BLUETOOTH -> R.string.constraint_cat_bluetooth + ConstraintCategory.DISPLAY -> R.string.constraint_cat_display + ConstraintCategory.FLASHLIGHT -> R.string.constraint_cat_flashlight + ConstraintCategory.WIFI -> R.string.constraint_cat_wifi + ConstraintCategory.KEYBOARD -> R.string.constraint_cat_keyboard + ConstraintCategory.LOCK -> R.string.constraint_cat_lock + ConstraintCategory.PHONE -> R.string.constraint_cat_phone + ConstraintCategory.POWER -> R.string.constraint_cat_power + ConstraintCategory.DEVICE -> R.string.constraint_cat_device + ConstraintCategory.TIME -> R.string.constraint_cat_time + } + + fun getCategory(constraintId: ConstraintId): ConstraintCategory = when (constraintId) { + ConstraintId.APP_IN_FOREGROUND, + ConstraintId.APP_NOT_IN_FOREGROUND, + ConstraintId.APP_PLAYING_MEDIA, + ConstraintId.APP_NOT_PLAYING_MEDIA, + -> ConstraintCategory.APPS + + ConstraintId.MEDIA_PLAYING, + ConstraintId.MEDIA_NOT_PLAYING, + -> ConstraintCategory.MEDIA + + ConstraintId.BT_DEVICE_CONNECTED, + ConstraintId.BT_DEVICE_DISCONNECTED, + -> ConstraintCategory.BLUETOOTH + + ConstraintId.SCREEN_ON, + ConstraintId.SCREEN_OFF, + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT, + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_270, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + -> ConstraintCategory.DISPLAY + + ConstraintId.FLASHLIGHT_ON, + ConstraintId.FLASHLIGHT_OFF, + -> ConstraintCategory.FLASHLIGHT + + ConstraintId.WIFI_ON, + ConstraintId.WIFI_OFF, + ConstraintId.WIFI_CONNECTED, + ConstraintId.WIFI_DISCONNECTED, + -> ConstraintCategory.WIFI + + ConstraintId.IME_CHOSEN, + ConstraintId.IME_NOT_CHOSEN, + ConstraintId.KEYBOARD_SHOWING, + ConstraintId.KEYBOARD_NOT_SHOWING, + -> ConstraintCategory.KEYBOARD + + ConstraintId.DEVICE_IS_LOCKED, + ConstraintId.DEVICE_IS_UNLOCKED, + ConstraintId.LOCK_SCREEN_SHOWING, + ConstraintId.LOCK_SCREEN_NOT_SHOWING, + -> ConstraintCategory.LOCK + + ConstraintId.IN_PHONE_CALL, + ConstraintId.NOT_IN_PHONE_CALL, + ConstraintId.PHONE_RINGING, + -> ConstraintCategory.PHONE + + ConstraintId.CHARGING, + ConstraintId.DISCHARGING, + -> ConstraintCategory.POWER + + ConstraintId.HINGE_CLOSED, + ConstraintId.HINGE_OPEN, + -> ConstraintCategory.DEVICE + + ConstraintId.TIME -> ConstraintCategory.TIME + } + fun getIcon(constraintId: ConstraintId): ComposeIconInfo = when (constraintId) { ConstraintId.APP_IN_FOREGROUND, ConstraintId.APP_NOT_IN_FOREGROUND, @@ -47,21 +131,29 @@ object ConstraintUtils { Icons.Outlined.BluetoothDisabled, ) - ConstraintId.ORIENTATION_0, - ConstraintId.ORIENTATION_180, + ConstraintId.DISPLAY_ORIENTATION_0, + ConstraintId.DISPLAY_ORIENTATION_180, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) - ConstraintId.ORIENTATION_90, - ConstraintId.ORIENTATION_270, + ConstraintId.DISPLAY_ORIENTATION_90, + ConstraintId.DISPLAY_ORIENTATION_270, -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) - ConstraintId.ORIENTATION_LANDSCAPE -> ComposeIconInfo.Vector( + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentLandscape, ) - ConstraintId.ORIENTATION_PORTRAIT -> ComposeIconInfo.Vector( + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> ComposeIconInfo.Vector( Icons.Outlined.StayCurrentPortrait, ) + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT, + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED, + -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) + + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE, + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED, + -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentLandscape) + ConstraintId.SCREEN_OFF -> ComposeIconInfo.Vector(Icons.Outlined.MobileOff) ConstraintId.SCREEN_ON -> ComposeIconInfo.Vector(Icons.Outlined.StayCurrentPortrait) @@ -114,12 +206,21 @@ object ConstraintUtils { R.string.constraint_choose_bluetooth_device_disconnected ConstraintId.SCREEN_ON -> R.string.constraint_choose_screen_on_description ConstraintId.SCREEN_OFF -> R.string.constraint_choose_screen_off_description - ConstraintId.ORIENTATION_PORTRAIT -> R.string.constraint_choose_orientation_portrait - ConstraintId.ORIENTATION_LANDSCAPE -> R.string.constraint_choose_orientation_landscape - ConstraintId.ORIENTATION_0 -> R.string.constraint_choose_orientation_0 - ConstraintId.ORIENTATION_90 -> R.string.constraint_choose_orientation_90 - ConstraintId.ORIENTATION_180 -> R.string.constraint_choose_orientation_180 - ConstraintId.ORIENTATION_270 -> R.string.constraint_choose_orientation_270 + ConstraintId.DISPLAY_ORIENTATION_PORTRAIT -> R.string.constraint_choose_orientation_portrait + ConstraintId.DISPLAY_ORIENTATION_LANDSCAPE -> + R.string.constraint_choose_orientation_landscape + ConstraintId.DISPLAY_ORIENTATION_0 -> R.string.constraint_choose_orientation_0 + ConstraintId.DISPLAY_ORIENTATION_90 -> R.string.constraint_choose_orientation_90 + ConstraintId.DISPLAY_ORIENTATION_180 -> R.string.constraint_choose_orientation_180 + ConstraintId.DISPLAY_ORIENTATION_270 -> R.string.constraint_choose_orientation_270 + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT -> + R.string.constraint_choose_physical_orientation_portrait + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE -> + R.string.constraint_choose_physical_orientation_landscape + ConstraintId.PHYSICAL_ORIENTATION_PORTRAIT_INVERTED -> + R.string.constraint_choose_physical_orientation_portrait_inverted + ConstraintId.PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED -> + R.string.constraint_choose_physical_orientation_landscape_inverted ConstraintId.FLASHLIGHT_ON -> R.string.constraint_flashlight_on ConstraintId.FLASHLIGHT_OFF -> R.string.constraint_flashlight_off ConstraintId.WIFI_ON -> R.string.constraint_wifi_on diff --git a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt index 87e38e6af3..8bf75d6f24 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/constraints/DetectConstraintsUseCase.kt @@ -71,6 +71,8 @@ class DetectConstraintsUseCaseImpl @AssistedInject constructor( ConstraintDependency.SCREEN_STATE -> displayAdapter.isScreenOn.map { dependency } ConstraintDependency.DISPLAY_ORIENTATION -> displayAdapter.orientation.map { dependency } + ConstraintDependency.PHYSICAL_ORIENTATION -> + displayAdapter.physicalOrientation.map { dependency } ConstraintDependency.FLASHLIGHT_STATE -> merge( cameraAdapter.isFlashlightOnFlow(CameraLens.FRONT), cameraAdapter.isFlashlightOnFlow(CameraLens.BACK), diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt index 97de63d1bd..befa0d8122 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/DetectKeyMapsUseCase.kt @@ -30,7 +30,6 @@ import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.system.popup.ToastAdapter import io.github.sds100.keymapper.system.vibrator.VibratorAdapter import io.github.sds100.keymapper.system.volume.VolumeAdapter -import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine @@ -49,17 +48,12 @@ class DetectKeyMapsUseCaseImpl @AssistedInject constructor( private val toastAdapter: ToastAdapter, private val resourceProvider: ResourceProvider, private val vibrator: VibratorAdapter, - @Assisted - private val coroutineScope: CoroutineScope, private val inputEventHub: InputEventHub, ) : DetectKeyMapsUseCase { @AssistedFactory interface Factory { - fun create( - accessibilityService: IAccessibilityService, - coroutineScope: CoroutineScope, - ): DetectKeyMapsUseCaseImpl + fun create(accessibilityService: IAccessibilityService): DetectKeyMapsUseCaseImpl } companion object { @@ -228,15 +222,15 @@ class DetectKeyMapsUseCaseImpl @AssistedInject constructor( } } - override fun imitateEvdevEvent(devicePath: String, type: Int, code: Int, value: Int) { + override fun imitateEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int) { if (inputEventHub.isSystemBridgeConnected()) { Timber.d( - "Imitate evdev event, device path: $devicePath, type: $type, code: $code, value: $value", + "Imitate evdev event, device id: $deviceId, type: $type, code: $code, value: $value", ) - inputEventHub.injectEvdevEvent(devicePath, type, code, value) + inputEventHub.injectEvdevEvent(deviceId, type, code, value) } else { Timber.w( - "Cannot imitate evdev event without system bridge connected. Device path: $devicePath, type: $type, code: $code, value: $value", + "Cannot imitate evdev event without system bridge connected.", ) } } @@ -268,5 +262,5 @@ interface DetectKeyMapsUseCase { source: Int = InputDevice.SOURCE_UNKNOWN, ) - fun imitateEvdevEvent(devicePath: String, type: Int, code: Int, value: Int) + fun imitateEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt index 7429079ace..b2b137abbe 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapAlgorithm.kt @@ -6,6 +6,7 @@ import androidx.collection.keyIterator import androidx.collection.valueIterator import io.github.sds100.keymapper.base.actions.Action import io.github.sds100.keymapper.base.actions.ActionData +import io.github.sds100.keymapper.base.actions.PerformActionTriggerDevice import io.github.sds100.keymapper.base.actions.PerformActionsUseCase import io.github.sds100.keymapper.base.constraints.ConstraintSnapshot import io.github.sds100.keymapper.base.constraints.ConstraintState @@ -97,7 +98,8 @@ class KeyMapAlgorithm( */ private var doublePressTimeoutTimes = longArrayOf() - private var actionMap: SparseArrayCompat = SparseArrayCompat() + var actionMap: SparseArrayCompat = SparseArrayCompat() + private set var triggers: Array = emptyArray() private set @@ -140,7 +142,8 @@ class KeyMapAlgorithm( * The actions to perform when each trigger is detected. The order matches with * [triggers]. */ - private var triggerActions: Array = arrayOf() + var triggerActions: Array = arrayOf() + private set /** * Stores whether each event in each parallel trigger need to be released after being held down. @@ -284,6 +287,7 @@ class KeyMapAlgorithm( val triggerActions = mutableListOf() val triggerConstraints = mutableListOf>() + val triggerPerformActionDevices = mutableListOf() val sequenceTriggerActionPerformers = mutableMapOf() @@ -648,13 +652,8 @@ class KeyMapAlgorithm( val event = EvdevEventAlgo( keyCode = inputEvent.androidCode, clickType = null, - devicePath = inputEvent.device.path, - device = EvdevDeviceInfo( - name = inputEvent.device.name, - bus = inputEvent.device.bus, - vendor = inputEvent.device.vendor, - product = inputEvent.device.product, - ), + deviceId = inputEvent.deviceId, + device = inputEvent.deviceInfo, scanCode = inputEvent.code, ) @@ -932,6 +931,7 @@ class KeyMapAlgorithm( performActionsAfterSequenceTriggerTimeout[triggerIndex] = performActionsAfterSequenceTriggerTimeout( + event, triggerIndex, overlappingSequenceTrigger, ) @@ -956,7 +956,7 @@ class KeyMapAlgorithm( oldJob?.cancel() parallelTriggerLongPressJobs.put( triggerIndex, - performActionsAfterLongPressDelay(triggerIndex), + performActionsAfterLongPressDelay(event, triggerIndex), ) } } @@ -1020,6 +1020,7 @@ class KeyMapAlgorithm( val trigger = triggers[triggerIndex] parallelTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), calledOnTriggerRelease = false, metaState = metaStateFromKeyEvent.withFlag(metaStateFromActions), ) @@ -1394,6 +1395,7 @@ class KeyMapAlgorithm( if (lastHeldDownEventIndex != triggers[triggerIndex].keys.lastIndex) { parallelTriggerActionPerformers[triggerIndex]?.onReleased( metaStateFromKeyEvent + metaStateFromActions, + device = event.performActionDevice(), ) } } @@ -1425,6 +1427,7 @@ class KeyMapAlgorithm( detectedSequenceTriggerIndexes.forEach { triggerIndex -> sequenceTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), metaState = metaStateFromActions.withFlag( metaStateFromKeyEvent, ), @@ -1433,6 +1436,7 @@ class KeyMapAlgorithm( detectedParallelTriggerIndexes.forEach { triggerIndex -> parallelTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), calledOnTriggerRelease = true, metaState = metaStateFromActions.withFlag(metaStateFromKeyEvent), ) @@ -1490,14 +1494,14 @@ class KeyMapAlgorithm( ) } else if (event is EvdevEventAlgo) { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, KMEvdevEvent.TYPE_KEY_EVENT, event.scanCode, KMEvdevEvent.VALUE_DOWN, ) useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, KMEvdevEvent.TYPE_KEY_EVENT, event.scanCode, KMEvdevEvent.VALUE_UP, @@ -1544,20 +1548,20 @@ class KeyMapAlgorithm( } else if (event is EvdevEventAlgo) { if (imitateUpKeyEvent) { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_UP, ) } else { useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_DOWN, ) useCase.imitateEvdevEvent( - devicePath = event.devicePath, + deviceId = event.deviceId, type = KMEvdevEvent.TYPE_KEY_EVENT, code = event.scanCode, value = KMEvdevEvent.VALUE_UP, @@ -1651,6 +1655,7 @@ class KeyMapAlgorithm( detectedTriggerIndexes.forEach { triggerIndex -> parallelTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), calledOnTriggerRelease = true, metaState = metaStateFromActions.withFlag(metaStateFromKeyEvent), ) @@ -1715,30 +1720,33 @@ class KeyMapAlgorithm( /** * For parallel triggers only. */ - private fun performActionsAfterLongPressDelay(triggerIndex: Int) = coroutineScope.launch { - delay(longPressDelay(triggers[triggerIndex])) + private fun performActionsAfterLongPressDelay(event: AlgoEvent, triggerIndex: Int) = + coroutineScope.launch { + delay(longPressDelay(triggers[triggerIndex])) - parallelTriggerActionPerformers[triggerIndex]?.onTriggered( - calledOnTriggerRelease = false, - metaState = metaStateFromActions.withFlag(metaStateFromKeyEvent), - ) + parallelTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), + calledOnTriggerRelease = false, + metaState = metaStateFromActions.withFlag(metaStateFromKeyEvent), + ) - if (triggers[triggerIndex].vibrate || - forceVibrate.value || - triggers[triggerIndex].longPressDoubleVibration - ) { - useCase.vibrate(vibrateDuration(triggers[triggerIndex])) - } + if (triggers[triggerIndex].vibrate || + forceVibrate.value || + triggers[triggerIndex].longPressDoubleVibration + ) { + useCase.vibrate(vibrateDuration(triggers[triggerIndex])) + } - if (triggers[triggerIndex].showToast) { - useCase.showTriggeredToast() + if (triggers[triggerIndex].showToast) { + useCase.showTriggeredToast() + } } - } /** * For parallel triggers only. */ private fun performActionsAfterSequenceTriggerTimeout( + event: AlgoEvent, triggerIndex: Int, sequenceTriggerIndex: Int, ) = coroutineScope.launch { @@ -1753,6 +1761,7 @@ class KeyMapAlgorithm( } parallelTriggerActionPerformers[triggerIndex]?.onTriggered( + device = event.performActionDevice(), calledOnTriggerRelease = true, metaState = metaStateFromActions.withFlag(metaStateFromKeyEvent), ) @@ -1944,7 +1953,7 @@ class KeyMapAlgorithm( } private data class EvdevEventAlgo( - val devicePath: String, + val deviceId: Int, val device: EvdevDeviceInfo, val scanCode: Int, val keyCode: Int, @@ -1978,4 +1987,11 @@ class KeyMapAlgorithm( ) : AlgoEvent() private data class TriggerKeyLocation(val triggerIndex: Int, val keyIndex: Int) + + private fun AlgoEvent.performActionDevice(): PerformActionTriggerDevice { + return when (this) { + is EvdevEventAlgo -> PerformActionTriggerDevice.Evdev(deviceId) + else -> PerformActionTriggerDevice.Default + } + } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt index 955023690b..61296637b4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionController.kt @@ -1,5 +1,6 @@ package io.github.sds100.keymapper.base.detection +import io.github.sds100.keymapper.base.actions.ActionData import io.github.sds100.keymapper.base.actions.PerformActionsUseCase import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCase import io.github.sds100.keymapper.base.input.InputEventDetectionSource @@ -11,7 +12,11 @@ import io.github.sds100.keymapper.base.trigger.AssistantTriggerType import io.github.sds100.keymapper.base.trigger.EvdevTriggerKey import io.github.sds100.keymapper.base.trigger.RecordTriggerController import io.github.sds100.keymapper.base.trigger.RecordTriggerState -import io.github.sds100.keymapper.base.trigger.Trigger +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +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.inputevents.KMEvdevEvent import io.github.sds100.keymapper.system.inputevents.KMInputEvent import kotlinx.coroutines.CoroutineScope @@ -19,6 +24,7 @@ import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.combine import kotlinx.coroutines.flow.launchIn +import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch import timber.log.Timber @@ -31,11 +37,61 @@ class KeyMapDetectionController( private val inputEventHub: InputEventHub, private val pauseKeyMapsUseCase: PauseKeyMapsUseCase, private val recordTriggerController: RecordTriggerController, + private val preferences: PreferenceRepository, ) : InputEventHubCallback { companion object { private const val INPUT_EVENT_HUB_ID = "key_map_controller" + + fun getEvdevGrabRequests( + algorithm: KeyMapAlgorithm, + injectKeyEventActionsWithSystemBridge: Boolean = true, + ): List { + val deviceKeyEventMap = mutableMapOf>() + + for ((index, trigger) in algorithm.triggers.withIndex()) { + val evdevDevices = trigger.keys.filterIsInstance() + .map { it.device } + .distinct() + .toList() + + if (evdevDevices.isEmpty()) { + continue + } + + val actions: List = algorithm.triggerActions[index] + .map { actionIndex -> algorithm.actionMap[actionIndex]?.data } + .filterNotNull() + + val extraKeyCodes = if (injectKeyEventActionsWithSystemBridge) { + actions + .filterIsInstance() + .map { it.keyCode } + } else { + emptyList() + } + + for (device in evdevDevices) { + deviceKeyEventMap.getOrPut(device) { mutableSetOf() }.addAll(extraKeyCodes) + } + } + + return deviceKeyEventMap.map { (device, keyEvents) -> + GrabTargetKeyCode( + name = device.name, + bus = device.bus, + vendor = device.vendor, + product = device.product, + extraKeyCodes = keyEvents.toIntArray(), + ) + } + } } + private val injectKeyEventsWithSystemBridge: StateFlow = + preferences.get(Keys.keyEventActionsUseSystemBridge) + .map { it ?: PreferenceDefaults.KEY_EVENT_ACTIONS_USE_SYSTEM_BRIDGE } + .stateIn(coroutineScope, SharingStarted.Eagerly, false) + private val algorithm: KeyMapAlgorithm = KeyMapAlgorithm(coroutineScope, detectUseCase, performActionsUseCase, detectConstraints) @@ -43,25 +99,41 @@ class KeyMapDetectionController( pauseKeyMapsUseCase.isPaused.stateIn(coroutineScope, SharingStarted.Eagerly, false) init { - // Must first register before collecting anything that may call reset() + // Must first register before collecting anything that may change the grabbed + // evdev devices. inputEventHub.registerClient(INPUT_EVENT_HUB_ID, this, listOf(KMEvdevEvent.TYPE_KEY_EVENT)) coroutineScope.launch { combine(detectUseCase.allKeyMapList, isPaused) { keyMapList, isPaused -> - algorithm.reset() - - if (isPaused) { - algorithm.loadKeyMaps(emptyList()) - inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList()) - } else { - algorithm.loadKeyMaps(keyMapList) - // Only grab the triggers that are actually being listened to by the algorithm - grabEvdevDevicesForTriggers(algorithm.triggers) - } + invalidateState(keyMapList, isPaused) }.launchIn(coroutineScope) } } + private fun invalidateState(keyMapList: List, isPaused: Boolean) { + algorithm.reset() + + if (isPaused) { + algorithm.loadKeyMaps(emptyList()) + inputEventHub.setGrabTargets(INPUT_EVENT_HUB_ID, emptyList()) + } else { + algorithm.loadKeyMaps(keyMapList) + // Determine which evdev devices need to be grabbed depending on the state + // of the algorithm. + val grabRequests = + getEvdevGrabRequests(algorithm, injectKeyEventsWithSystemBridge.value) + + Timber.i( + "Grab evdev devices for key map detection: ${grabRequests.joinToString()}", + ) + + inputEventHub.setGrabTargets( + INPUT_EVENT_HUB_ID, + grabRequests, + ) + } + } + override fun onInputEvent( event: KMInputEvent, detectionSource: InputEventDetectionSource, @@ -94,26 +166,8 @@ class KeyMapDetectionController( } fun teardown() { - reset() - inputEventHub.unregisterClient(INPUT_EVENT_HUB_ID) - } - - private fun grabEvdevDevicesForTriggers(triggers: Array) { - val evdevDevices = triggers - .flatMap { trigger -> trigger.keys.filterIsInstance() } - .map { it.device } - .distinct() - .toList() - - Timber.i("Grab evdev devices for key map detection: ${evdevDevices.joinToString()}") - inputEventHub.setGrabbedEvdevDevices( - INPUT_EVENT_HUB_ID, - evdevDevices, - ) - } - - private fun reset() { algorithm.reset() - inputEventHub.setGrabbedEvdevDevices(INPUT_EVENT_HUB_ID, emptyList()) + inputEventHub.setGrabTargets(INPUT_EVENT_HUB_ID, emptyList()) + inputEventHub.unregisterClient(INPUT_EVENT_HUB_ID) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/ParallelTriggerActionPerformer.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/ParallelTriggerActionPerformer.kt index a4b46b0833..ad9e255233 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/ParallelTriggerActionPerformer.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/ParallelTriggerActionPerformer.kt @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.base.detection import io.github.sds100.keymapper.base.actions.Action import io.github.sds100.keymapper.base.actions.ActionData +import io.github.sds100.keymapper.base.actions.PerformActionTriggerDevice import io.github.sds100.keymapper.base.actions.PerformActionsUseCase import io.github.sds100.keymapper.base.actions.RepeatMode import io.github.sds100.keymapper.common.utils.InputEventAction @@ -47,7 +48,11 @@ class ParallelTriggerActionPerformer( PreferenceDefaults.REPEAT_RATE.toLong(), ) - fun onTriggered(calledOnTriggerRelease: Boolean, metaState: Int) { + fun onTriggered( + calledOnTriggerRelease: Boolean, + metaState: Int, + device: PerformActionTriggerDevice, + ) { performActionsJob?.cancel() /* this job shouldn't be cancelled when the trigger is released. all actions should be performed @@ -84,7 +89,7 @@ class ParallelTriggerActionPerformer( else -> InputEventAction.DOWN_UP } - performAction(action, actionInputEventAction, metaState) + performAction(action, actionInputEventAction, metaState, device) if (action.repeat && action.holdDown) { delay(action.holdDownDuration?.toLong() ?: defaultHoldDownDuration.value) @@ -131,13 +136,13 @@ class ParallelTriggerActionPerformer( while (isActive && continueRepeating) { if (action.holdDown) { - performAction(action, InputEventAction.DOWN, metaState) + performAction(action, InputEventAction.DOWN, metaState, device) delay( action.holdDownDuration?.toLong() ?: defaultHoldDownDuration.value, ) - performAction(action, InputEventAction.UP, metaState) + performAction(action, InputEventAction.UP, metaState, device) } else { - performAction(action, InputEventAction.DOWN_UP, metaState) + performAction(action, InputEventAction.DOWN_UP, metaState, device) } repeatCount++ @@ -152,7 +157,7 @@ class ParallelTriggerActionPerformer( } } - fun onReleased(metaState: Int) { + fun onReleased(metaState: Int, device: PerformActionTriggerDevice) { repeatJobs.forEachIndexed { actionIndex, job -> if (actionList[actionIndex].repeatMode == RepeatMode.TRIGGER_RELEASED) { job?.cancel() @@ -166,7 +171,7 @@ class ParallelTriggerActionPerformer( if (actionIsHeldDown[actionIndex]) { actionIsHeldDown[actionIndex] = false - performAction(action, InputEventAction.UP, metaState) + performAction(action, InputEventAction.UP, metaState, device) } } } @@ -180,7 +185,12 @@ class ParallelTriggerActionPerformer( coroutineScope.launch { for ((index, isHeldDown) in actionIsHeldDown.withIndex()) { if (isHeldDown) { - performAction(actionList[index], inputEventAction = InputEventAction.UP, 0) + performAction( + actionList[index], + inputEventAction = InputEventAction.UP, + 0, + PerformActionTriggerDevice.Default, + ) } } } @@ -199,9 +209,10 @@ class ParallelTriggerActionPerformer( action: Action, inputEventAction: InputEventAction, metaState: Int, + device: PerformActionTriggerDevice, ) { repeat(action.multiplier ?: 1) { - useCase.perform(action.data, inputEventAction, metaState) + useCase.perform(action.data, inputEventAction, metaState, device) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/detection/SequenceTriggerActionPerformer.kt b/base/src/main/java/io/github/sds100/keymapper/base/detection/SequenceTriggerActionPerformer.kt index dc7d40816b..c17dc2ffdf 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/detection/SequenceTriggerActionPerformer.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/detection/SequenceTriggerActionPerformer.kt @@ -1,6 +1,7 @@ package io.github.sds100.keymapper.base.detection import io.github.sds100.keymapper.base.actions.Action +import io.github.sds100.keymapper.base.actions.PerformActionTriggerDevice import io.github.sds100.keymapper.base.actions.PerformActionsUseCase import io.github.sds100.keymapper.common.utils.InputEventAction import kotlinx.coroutines.CoroutineScope @@ -15,7 +16,7 @@ class SequenceTriggerActionPerformer( ) { private var job: Job? = null - fun onTriggered(metaState: Int) { + fun onTriggered(device: PerformActionTriggerDevice, metaState: Int) { /* this job shouldn't be cancelled when the trigger is released. all actions should be performed once before repeating (if configured). @@ -23,7 +24,7 @@ class SequenceTriggerActionPerformer( job?.cancel() job = coroutineScope.launch { for (action in actionList) { - performAction(action, metaState) + performAction(action, metaState, device) delay(action.delayBeforeNextAction?.toLong() ?: 0L) } @@ -35,9 +36,13 @@ class SequenceTriggerActionPerformer( job = null } - private suspend fun performAction(action: Action, metaState: Int) { + private suspend fun performAction( + action: Action, + metaState: Int, + device: PerformActionTriggerDevice, + ) { repeat(action.multiplier ?: 1) { - useCase.perform(action.data, InputEventAction.DOWN_UP, metaState) + useCase.perform(action.data, InputEventAction.DOWN_UP, metaState, device) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt similarity index 81% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt index 719c2cfabb..496c22c917 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeScreen.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.os.Build import android.provider.Settings @@ -44,6 +44,7 @@ import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.Icon import androidx.compose.material3.IconButton +import androidx.compose.material3.LocalContentColor import androidx.compose.material3.MaterialTheme import androidx.compose.material3.OutlinedCard import androidx.compose.material3.Scaffold @@ -75,20 +76,20 @@ import io.github.sds100.keymapper.common.utils.SettingsUtils import io.github.sds100.keymapper.common.utils.State @Composable -fun ProModeScreen(modifier: Modifier = Modifier, viewModel: ProModeViewModel) { - val proModeWarningState by viewModel.warningState.collectAsStateWithLifecycle() - val proModeSetupState by viewModel.setupState.collectAsStateWithLifecycle() +fun ExpertModeScreen(modifier: Modifier = Modifier, viewModel: ExpertModeViewModel) { + val expertModeWarningState by viewModel.warningState.collectAsStateWithLifecycle() + val expertModeSetupState by viewModel.setupState.collectAsStateWithLifecycle() val autoStartBootEnabled by viewModel.autoStartBootEnabled.collectAsStateWithLifecycle() - ProModeScreen( + ExpertModeScreen( modifier = modifier, onBackClick = viewModel::onBackClick, onHelpClick = { viewModel.showInfoCard() }, showHelpIcon = !viewModel.showInfoCard, ) { Content( - warningState = proModeWarningState, - setupState = proModeSetupState, + warningState = expertModeWarningState, + setupState = expertModeSetupState, showInfoCard = viewModel.showInfoCard, onInfoCardDismiss = { viewModel.hideInfoCard() }, onWarningButtonClick = viewModel::onWarningButtonClick, @@ -105,7 +106,7 @@ fun ProModeScreen(modifier: Modifier = Modifier, viewModel: ProModeViewModel) { @OptIn(ExperimentalMaterial3Api::class) @Composable -private fun ProModeScreen( +private fun ExpertModeScreen( modifier: Modifier = Modifier, onBackClick: () -> Unit = {}, onHelpClick: () -> Unit = {}, @@ -116,7 +117,7 @@ private fun ProModeScreen( modifier = modifier.displayCutoutPadding(), topBar = { TopAppBar( - title = { Text(stringResource(R.string.pro_mode_app_bar_title)) }, + title = { Text(stringResource(R.string.expert_mode_app_bar_title)) }, actions = { AnimatedVisibility( visible = showHelpIcon, @@ -127,7 +128,7 @@ private fun ProModeScreen( Icon( imageVector = Icons.AutoMirrored.Rounded.HelpOutline, contentDescription = stringResource( - R.string.pro_mode_info_card_show_content_description, + R.string.expert_mode_info_card_show_content_description, ), ) } @@ -168,8 +169,8 @@ private fun ProModeScreen( @Composable private fun Content( modifier: Modifier = Modifier, - warningState: ProModeWarningState, - setupState: State, + warningState: ExpertModeWarningState, + setupState: State, showInfoCard: Boolean, onInfoCardDismiss: () -> Unit = {}, onWarningButtonClick: () -> Unit = {}, @@ -187,7 +188,7 @@ private fun Content( enter = fadeIn() + expandVertically(), exit = fadeOut() + shrinkVertically(), ) { - ProModeInfoCard( + ExpertModeInfoCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp), @@ -209,7 +210,7 @@ private fun Content( Spacer(modifier = Modifier.height(16.dp)) - if (warningState is ProModeWarningState.Understood) { + if (warningState is ExpertModeWarningState.Understood) { when (setupState) { is State.Loading -> { CircularProgressIndicator( @@ -234,7 +235,7 @@ private fun Content( } else { Text( modifier = Modifier.padding(horizontal = 32.dp), - text = stringResource(R.string.pro_mode_settings_unavailable_text), + text = stringResource(R.string.expert_mode_settings_unavailable_text), textAlign = TextAlign.Center, ) } @@ -244,7 +245,7 @@ private fun Content( @Composable private fun LoadedContent( modifier: Modifier, - state: ProModeState, + state: ExpertModeState, onRootButtonClick: () -> Unit = {}, onShizukuButtonClick: () -> Unit, onStopServiceClick: () -> Unit, @@ -257,16 +258,16 @@ private fun LoadedContent( OptionsHeaderRow( modifier = Modifier.padding(horizontal = 16.dp), icon = Icons.Rounded.Checklist, - text = stringResource(R.string.pro_mode_set_up_title), + text = stringResource(R.string.expert_mode_set_up_title), ) Spacer(modifier = Modifier.height(8.dp)) // Show notification permission warning if permission not granted - if (state is ProModeState.Stopped && !state.isNotificationPermissionGranted) { + if (state is ExpertModeState.Stopped && !state.isNotificationPermissionGranted) { val text = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_description, + R.string.expert_mode_setup_wizard_enable_notification_permission_description, ) SetupCard( @@ -282,7 +283,7 @@ private fun LoadedContent( ) }, title = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_title, + R.string.expert_mode_setup_wizard_enable_notification_permission_title, ), content = { Text( @@ -291,7 +292,7 @@ private fun LoadedContent( ) }, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_button, + R.string.expert_mode_setup_wizard_enable_notification_permission_button, ), onButtonClick = onRequestNotificationPermissionClick, ) @@ -299,7 +300,7 @@ private fun LoadedContent( } when (state) { - is ProModeState.Started -> { + is ExpertModeState.Started -> { if (!state.isDefaultUsbModeCompatible) { IncompatibleUsbModeCard( modifier = Modifier @@ -310,7 +311,7 @@ private fun LoadedContent( Spacer(Modifier.height(8.dp)) } - ProModeStartedCard( + ExpertModeStartedCard( modifier = Modifier .fillMaxWidth() .padding(horizontal = 8.dp), @@ -318,7 +319,7 @@ private fun LoadedContent( ) } - is ProModeState.Stopped -> { + is ExpertModeState.Stopped -> { if (state.isRootGranted) { SetupCard( modifier = Modifier @@ -332,18 +333,19 @@ private fun LoadedContent( tint = LocalCustomColorsPalette.current.magiskTeal, ) }, - title = stringResource(R.string.pro_mode_root_detected_title), + title = stringResource(R.string.expert_mode_root_detected_title), content = { Text( - text = stringResource(R.string.pro_mode_root_detected_text), + text = stringResource(R.string.expert_mode_root_detected_text), style = MaterialTheme.typography.bodyMedium, ) }, buttonText = stringResource( - R.string.pro_mode_root_detected_button_start_service, + R.string.expert_mode_root_detected_button_start_service, ), onButtonClick = onRootButtonClick, enabled = state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) Spacer(modifier = Modifier.height(8.dp)) @@ -351,14 +353,17 @@ private fun LoadedContent( val shizukuButtonText: String? = when (state.shizukuSetupState) { ShizukuSetupState.INSTALLED -> stringResource( - R.string.pro_mode_shizuku_detected_button_start, + R.string.expert_mode_shizuku_detected_button_start, ) + ShizukuSetupState.STARTED -> stringResource( - R.string.pro_mode_shizuku_detected_button_request_permission, + R.string.expert_mode_shizuku_detected_button_request_permission, ) + ShizukuSetupState.PERMISSION_GRANTED -> stringResource( - R.string.pro_mode_shizuku_detected_button_start_service, + R.string.expert_mode_shizuku_detected_button_start_service, ) + ShizukuSetupState.NOT_FOUND -> null } @@ -374,16 +379,17 @@ private fun LoadedContent( contentDescription = null, ) }, - title = stringResource(R.string.pro_mode_shizuku_detected_title), + title = stringResource(R.string.expert_mode_shizuku_detected_title), content = { Text( - text = stringResource(R.string.pro_mode_shizuku_detected_text), + text = stringResource(R.string.expert_mode_shizuku_detected_text), style = MaterialTheme.typography.bodyMedium, ) }, buttonText = shizukuButtonText, onButtonClick = onShizukuButtonClick, enabled = state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) } @@ -391,9 +397,10 @@ private fun LoadedContent( val setupKeyMapperText: String = when { Build.VERSION.SDK_INT < Build.VERSION_CODES.R -> stringResource( - R.string.pro_mode_set_up_with_key_mapper_button_incompatible, + R.string.expert_mode_set_up_with_key_mapper_button_incompatible, ) - else -> stringResource(R.string.pro_mode_set_up_with_key_mapper_button) + + else -> stringResource(R.string.expert_mode_set_up_with_key_mapper_button) } SetupCard( @@ -408,13 +415,14 @@ private fun LoadedContent( contentDescription = null, ) }, - title = stringResource(R.string.pro_mode_set_up_with_key_mapper_title), + title = stringResource(R.string.expert_mode_set_up_with_key_mapper_title), content = {}, buttonText = setupKeyMapperText, onButtonClick = onSetupWithKeyMapperClick, enabled = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R && state.isNotificationPermissionGranted, + isLoading = state.isStarting, ) } } @@ -425,15 +433,15 @@ private fun LoadedContent( OptionsHeaderRow( modifier = Modifier.padding(horizontal = 16.dp), icon = Icons.Rounded.Tune, - text = stringResource(R.string.pro_mode_options_title), + text = stringResource(R.string.expert_mode_options_title), ) Spacer(modifier = Modifier.height(8.dp)) SwitchPreferenceCompose( modifier = Modifier.padding(horizontal = 8.dp), - title = stringResource(R.string.title_pref_pro_mode_auto_start_at_boot), - text = stringResource(R.string.summary_pref_pro_mode_auto_start_at_boot), + title = stringResource(R.string.title_pref_expert_mode_auto_start), + text = stringResource(R.string.summary_pref_expert_mode_auto_start), icon = Icons.Rounded.RestartAlt, isChecked = autoStartAtBoot, onCheckedChange = onAutoStartAtBootToggled, @@ -457,12 +465,12 @@ private fun IncompatibleUsbModeCard(modifier: Modifier = Modifier) { ) }, title = stringResource( - R.string.pro_mode_setup_wizard_change_default_usb_configuration_title, + R.string.expert_mode_setup_wizard_change_default_usb_configuration_title, ), content = { Text( text = stringResource( - R.string.pro_mode_setup_wizard_change_default_usb_configuration_description, + R.string.expert_mode_setup_wizard_change_default_usb_configuration_description, ), style = MaterialTheme.typography.bodyMedium, ) @@ -484,10 +492,10 @@ private fun IncompatibleUsbModeCard(modifier: Modifier = Modifier) { @Composable private fun WarningCard( modifier: Modifier = Modifier, - state: ProModeWarningState, + state: ExpertModeWarningState, onButtonClick: () -> Unit = {}, ) { - val borderStroke = if (state is ProModeWarningState.Understood) { + val borderStroke = if (state is ExpertModeWarningState.Understood) { CardDefaults.outlinedCardBorder() } else { BorderStroke(1.dp, MaterialTheme.colorScheme.error) @@ -509,7 +517,7 @@ private fun WarningCard( Spacer(modifier = Modifier.width(8.dp)) Text( - text = stringResource(R.string.pro_mode_warning_title), + text = stringResource(R.string.expert_mode_warning_title), style = MaterialTheme.typography.titleMedium, ) } @@ -518,7 +526,7 @@ private fun WarningCard( Text( modifier = Modifier.padding(horizontal = 16.dp), - text = stringResource(R.string.pro_mode_warning_text), + text = stringResource(R.string.expert_mode_warning_text), style = MaterialTheme.typography.bodyMedium, ) @@ -529,29 +537,30 @@ private fun WarningCard( .align(Alignment.End) .padding(horizontal = 16.dp), onClick = onButtonClick, - enabled = state is ProModeWarningState.Idle, + enabled = state is ExpertModeWarningState.Idle, colors = ButtonDefaults.filledTonalButtonColors( containerColor = MaterialTheme.colorScheme.error, contentColor = MaterialTheme.colorScheme.onError, ), ) { - if (state is ProModeWarningState.Understood) { + if (state is ExpertModeWarningState.Understood) { Icon(imageVector = Icons.Rounded.Check, contentDescription = null) Spacer(modifier = Modifier.width(8.dp)) } val text = when (state) { - is ProModeWarningState.CountingDown -> stringResource( - R.string.pro_mode_warning_understand_button_countdown, + is ExpertModeWarningState.CountingDown -> stringResource( + R.string.expert_mode_warning_understand_button_countdown, state.seconds, ) - ProModeWarningState.Idle -> stringResource( - R.string.pro_mode_warning_understand_button_not_completed, + ExpertModeWarningState.Idle -> stringResource( + R.string.expert_mode_warning_understand_button_not_completed, ) - ProModeWarningState.Understood -> stringResource( - R.string.pro_mode_warning_understand_button_completed, + + ExpertModeWarningState.Understood -> stringResource( + R.string.expert_mode_warning_understand_button_completed, ) } @@ -563,7 +572,7 @@ private fun WarningCard( } @Composable -private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> Unit = {}) { +private fun ExpertModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> Unit = {}) { OutlinedCard(modifier) { Row( verticalAlignment = Alignment.CenterVertically, @@ -582,7 +591,7 @@ private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> modifier = Modifier .weight(1f) .padding(vertical = 8.dp), - text = stringResource(R.string.pro_mode_service_started), + text = stringResource(R.string.expert_mode_service_started), style = MaterialTheme.typography.titleMedium, ) @@ -594,7 +603,7 @@ private fun ProModeStartedCard(modifier: Modifier = Modifier, onStopClick: () -> contentColor = MaterialTheme.colorScheme.error, ), ) { - Text(stringResource(R.string.pro_mode_stop_service_button)) + Text(stringResource(R.string.expert_mode_stop_service_button)) } Spacer(modifier = Modifier.width(16.dp)) @@ -612,6 +621,7 @@ private fun SetupCard( buttonText: String, onButtonClick: () -> Unit = {}, enabled: Boolean = true, + isLoading: Boolean = false, ) { OutlinedCard(modifier = modifier) { Spacer(modifier = Modifier.height(16.dp)) @@ -645,12 +655,20 @@ private fun SetupCard( .align(Alignment.End) .padding(horizontal = 16.dp), onClick = onButtonClick, - enabled = enabled, + enabled = enabled && !isLoading, colors = ButtonDefaults.filledTonalButtonColors( containerColor = color, contentColor = LocalCustomColorsPalette.current.contentColorFor(color), ), ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(18.dp), + strokeWidth = 2.dp, + color = LocalContentColor.current, + ) + Spacer(modifier = Modifier.width(8.dp)) + } Text(buttonText) } @@ -659,7 +677,7 @@ private fun SetupCard( } @Composable -private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit = {}) { +private fun ExpertModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit = {}) { OutlinedCard( modifier = modifier, border = BorderStroke(1.dp, MaterialTheme.colorScheme.primary), @@ -682,7 +700,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Spacer(modifier = Modifier.width(8.dp)) Text( - text = stringResource(R.string.pro_mode_info_card_title), + text = stringResource(R.string.expert_mode_info_card_title), style = MaterialTheme.typography.titleMedium, color = MaterialTheme.colorScheme.onSurface, ) @@ -691,7 +709,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Spacer(modifier = Modifier.height(8.dp)) Text( - text = stringResource(R.string.pro_mode_info_card_description), + text = stringResource(R.string.expert_mode_info_card_description), style = MaterialTheme.typography.bodyMedium, ) } @@ -705,7 +723,7 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit Icon( imageVector = Icons.Rounded.Close, contentDescription = stringResource( - R.string.pro_mode_info_card_dismiss_content_description, + R.string.expert_mode_info_card_dismiss_content_description, ), tint = MaterialTheme.colorScheme.onSurfaceVariant, ) @@ -718,14 +736,15 @@ private fun ProModeInfoCard(modifier: Modifier = Modifier, onDismiss: () -> Unit @Composable private fun Preview() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, + warningState = ExpertModeWarningState.Understood, setupState = State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = false, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = true, + isStarting = false, ), ), showInfoCard = true, @@ -741,10 +760,10 @@ private fun Preview() { @Composable private fun PreviewDark() { KeyMapperTheme(darkTheme = true) { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, - setupState = State.Data(ProModeState.Started(isDefaultUsbModeCompatible = true)), + warningState = ExpertModeWarningState.Understood, + setupState = State.Data(ExpertModeState.Started(isDefaultUsbModeCompatible = true)), showInfoCard = false, onInfoCardDismiss = {}, autoStartAtBoot = true, @@ -758,9 +777,9 @@ private fun PreviewDark() { @Composable private fun PreviewCountingDown() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.CountingDown( + warningState = ExpertModeWarningState.CountingDown( seconds = 5, ), setupState = State.Loading, @@ -777,10 +796,12 @@ private fun PreviewCountingDown() { @Composable private fun PreviewStarted() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, - setupState = State.Data(ProModeState.Started(isDefaultUsbModeCompatible = false)), + warningState = ExpertModeWarningState.Understood, + setupState = State.Data( + ExpertModeState.Started(isDefaultUsbModeCompatible = false), + ), showInfoCard = false, onInfoCardDismiss = {}, autoStartAtBoot = false, @@ -794,14 +815,15 @@ private fun PreviewStarted() { @Composable private fun PreviewNotificationPermissionNotGranted() { KeyMapperTheme { - ProModeScreen { + ExpertModeScreen { Content( - warningState = ProModeWarningState.Understood, + warningState = ExpertModeWarningState.Understood, setupState = State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = true, shizukuSetupState = ShizukuSetupState.PERMISSION_GRANTED, isNotificationPermissionGranted = false, + isStarting = false, ), ), showInfoCard = false, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt new file mode 100644 index 0000000000..2d1c491ad9 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupDelegateImpl.kt @@ -0,0 +1,28 @@ +package io.github.sds100.keymapper.base.expertmode + +import dagger.hilt.android.scopes.ViewModelScoped +import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider +import io.github.sds100.keymapper.base.utils.ui.ResourceProvider +import javax.inject.Inject +import javax.inject.Named +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.launch + +@ViewModelScoped +class ExpertModeSetupDelegateImpl @Inject constructor( + @Named("viewmodel") + viewModelScope: CoroutineScope, + useCase: SystemBridgeSetupUseCase, + resourceProvider: ResourceProvider, + private val navigationProvider: NavigationProvider, +) : SystemBridgeSetupDelegateImpl( + viewModelScope, + useCase, + resourceProvider, +) { + override fun onFinishClick() { + viewModelScope.launch { + navigationProvider.popBackStack() + } + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt similarity index 50% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt index 8b49b0cd9a..aba3e9398d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupScreen.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.content.res.Configuration import androidx.compose.animation.core.Animatable @@ -14,6 +14,7 @@ import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size +import androidx.compose.foundation.layout.width import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.verticalScroll import androidx.compose.material.icons.Icons @@ -61,13 +62,13 @@ import io.github.sds100.keymapper.common.utils.State import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep @Composable -fun ProModeSetupScreen(viewModel: ProModeSetupViewModel) { +fun ExpertModeSetupScreen(viewModel: ExpertModeSetupViewModel) { val state by viewModel.setupState.collectAsStateWithLifecycle() - ProModeSetupScreen( + ExpertModeSetupScreen( state = state, - onStepButtonClick = viewModel::onStepButtonClick, - onAssistantClick = viewModel::onAssistantClick, + onStepButtonClick = viewModel::onSetupStepButtonClick, + onAssistantClick = viewModel::onSetupAssistantClick, onWatchTutorialClick = { }, onBackClick = viewModel::onBackClick, ) @@ -75,8 +76,8 @@ fun ProModeSetupScreen(viewModel: ProModeSetupViewModel) { @OptIn(ExperimentalMaterial3Api::class) @Composable -fun ProModeSetupScreen( - state: State, +fun ExpertModeSetupScreen( + state: State, onBackClick: () -> Unit = {}, onStepButtonClick: () -> Unit = {}, onAssistantClick: () -> Unit = {}, @@ -85,7 +86,7 @@ fun ProModeSetupScreen( Scaffold( topBar = { TopAppBar( - title = { Text(stringResource(R.string.pro_mode_setup_wizard_title)) }, + title = { Text(stringResource(R.string.expert_mode_setup_wizard_title)) }, navigationIcon = { IconButton(onClick = onBackClick) { Icon( @@ -97,105 +98,121 @@ fun ProModeSetupScreen( ) }, ) { paddingValues -> - when (state) { - State.Loading -> { - Box( - Modifier - .padding(paddingValues) - .fillMaxSize(), - contentAlignment = Alignment.Center, - ) { - CircularProgressIndicator() - } + ExpertModeSetupScreenContent( + modifier = Modifier + .padding(paddingValues) + .fillMaxSize(), + state, + onAssistantClick, + onWatchTutorialClick, + onStepButtonClick, + ) + } +} + +@Composable +fun ExpertModeSetupScreenContent( + modifier: Modifier = Modifier, + state: State, + onAssistantClick: () -> Unit, + onWatchTutorialClick: () -> Unit, + onStepButtonClick: () -> Unit, +) { + when (state) { + State.Loading -> { + Box( + modifier, + contentAlignment = Alignment.Center, + ) { + CircularProgressIndicator() } + } - is State.Data -> { - val stepContent = getStepContent(state.data.step) + is State.Data -> { + val stepContent = state.data.stepContent + + // Create animated progress for entrance and updates + val progressAnimatable = remember { Animatable(0f) } + val targetProgress = state.data.stepNumber.toFloat() / (state.data.stepCount) + + // Animate progress when it changes + LaunchedEffect(targetProgress) { + progressAnimatable.animateTo( + targetValue = targetProgress, + animationSpec = tween( + durationMillis = 800, + easing = EaseInOut, + ), + ) + } - // Create animated progress for entrance and updates - val progressAnimatable = remember { Animatable(0f) } - val targetProgress = state.data.stepNumber.toFloat() / (state.data.stepCount) + // Animate entrance when screen opens + LaunchedEffect(Unit) { + progressAnimatable.animateTo( + targetValue = targetProgress, + animationSpec = tween( + durationMillis = 1000, + easing = EaseInOut, + ), + ) + } - // Animate progress when it changes - LaunchedEffect(targetProgress) { - progressAnimatable.animateTo( - targetValue = targetProgress, - animationSpec = tween( - durationMillis = 800, - easing = EaseInOut, - ), - ) - } + Column( + modifier = modifier + .padding(vertical = 16.dp, horizontal = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally, + ) { + LinearProgressIndicator( + modifier = Modifier.fillMaxWidth(), + progress = { progressAnimatable.value }, + ) - // Animate entrance when screen opens - LaunchedEffect(Unit) { - progressAnimatable.animateTo( - targetValue = targetProgress, - animationSpec = tween( - durationMillis = 1000, - easing = EaseInOut, - ), - ) - } + Spacer(modifier = Modifier.height(16.dp)) - Column( - modifier = Modifier - .fillMaxSize() - .padding(paddingValues) - .padding(vertical = 16.dp, horizontal = 16.dp), - horizontalAlignment = Alignment.CenterHorizontally, + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically, ) { - LinearProgressIndicator( - modifier = Modifier.fillMaxWidth(), - progress = { progressAnimatable.value }, + Text( + text = stringResource( + R.string.expert_mode_setup_wizard_step_n, + state.data.stepNumber, + state.data.stepCount, + ), + style = MaterialTheme.typography.titleLarge, ) - - Spacer(modifier = Modifier.height(16.dp)) - - Row( - modifier = Modifier.fillMaxWidth(), - horizontalArrangement = Arrangement.SpaceBetween, - verticalAlignment = Alignment.CenterVertically, - ) { - Text( - text = stringResource( - R.string.pro_mode_setup_wizard_step_n, - state.data.stepNumber, - state.data.stepCount, - ), - style = MaterialTheme.typography.titleLarge, - ) - Text( - text = stringResource(R.string.pro_mode_app_bar_title), - style = MaterialTheme.typography.titleLarge, - ) - } - Spacer(modifier = Modifier.height(16.dp)) - - AssistantCheckBoxRow( - modifier = Modifier.fillMaxWidth(), - isEnabled = state.data.isSetupAssistantButtonEnabled, - isChecked = state.data.isSetupAssistantChecked, - onAssistantClick = onAssistantClick, + Text( + text = stringResource(R.string.expert_mode_setup_title), + style = MaterialTheme.typography.titleLarge, ) + } + Spacer(modifier = Modifier.height(16.dp)) - val iconTint = if (state.data.step == SystemBridgeSetupStep.STARTED) { - LocalCustomColorsPalette.current.green - } else { - MaterialTheme.colorScheme.onSurface - } + AssistantCheckBoxRow( + modifier = Modifier.fillMaxWidth(), + isEnabled = state.data.isSetupAssistantButtonEnabled, + isChecked = state.data.isSetupAssistantChecked, + onAssistantClick = onAssistantClick, + ) - StepContent( - modifier = Modifier - .fillMaxWidth() - .weight(1f) - .padding(horizontal = 16.dp), - stepContent, - onWatchTutorialClick, - onStepButtonClick, - iconTint = iconTint, - ) + val iconTint = if (state.data.step == SystemBridgeSetupStep.STARTED) { + LocalCustomColorsPalette.current.green + } else { + MaterialTheme.colorScheme.onSurface } + + StepContent( + modifier = Modifier + .fillMaxWidth() + .weight(1f) + .padding(horizontal = 16.dp), + stepContent = stepContent, + onWatchTutorialClick = onWatchTutorialClick, + onButtonClick = onStepButtonClick, + iconTint = iconTint, + isLoading = state.data.isStarting, + ) } } } @@ -208,6 +225,7 @@ private fun StepContent( onWatchTutorialClick: () -> Unit, onButtonClick: () -> Unit, iconTint: Color = Color.Unspecified, + isLoading: Boolean = false, ) { Column( modifier, @@ -252,9 +270,20 @@ private fun StepContent( verticalAlignment = Alignment.CenterVertically, ) { // TextButton(onClick = onWatchTutorialClick) { -// Text(text = stringResource(R.string.pro_mode_setup_wizard_watch_tutorial_button)) +// Text(text = stringResource(R.string.expert_mode_setup_wizard_watch_tutorial_button)) // } - Button(onClick = onButtonClick) { + Button( + onClick = onButtonClick, + enabled = !isLoading, + ) { + if (isLoading) { + CircularProgressIndicator( + modifier = Modifier.size(18.dp), + strokeWidth = 2.dp, + color = LocalContentColor.current, + ) + Spacer(modifier = Modifier.width(8.dp)) + } Text(text = stepContent.buttonText) } } @@ -292,16 +321,16 @@ private fun AssistantCheckBoxRow( ) val text = if (isEnabled) { - stringResource(R.string.pro_mode_setup_wizard_use_assistant_description) + stringResource(R.string.expert_mode_setup_wizard_use_assistant_description) } else { stringResource( - R.string.pro_mode_setup_wizard_use_assistant_enable_accessibility_service, + R.string.expert_mode_setup_wizard_use_assistant_enable_service, ) } Column { Text( - text = stringResource(R.string.pro_mode_setup_wizard_use_assistant), + text = stringResource(R.string.expert_mode_setup_wizard_use_assistant), style = MaterialTheme.typography.titleMedium, ) @@ -316,103 +345,118 @@ private fun AssistantCheckBoxRow( } @Composable -private fun getStepContent(step: SystemBridgeSetupStep): StepContent { +private fun getIconForStep(step: SystemBridgeSetupStep): ImageVector { + return when (step) { + SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> Icons.Rounded.Accessibility + SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> Icons.Rounded.Notifications + SystemBridgeSetupStep.DEVELOPER_OPTIONS -> Icons.Rounded.Build + SystemBridgeSetupStep.WIFI_NETWORK -> KeyMapperIcons.SignalWifiNotConnected + SystemBridgeSetupStep.WIRELESS_DEBUGGING -> Icons.Rounded.BugReport + SystemBridgeSetupStep.ADB_PAIRING -> Icons.Rounded.Link + SystemBridgeSetupStep.START_SERVICE -> Icons.Rounded.PlayArrow + SystemBridgeSetupStep.STARTED -> Icons.Rounded.CheckCircleOutline + } +} + +@Composable +private fun createPreviewStepContent(step: SystemBridgeSetupStep): StepContent { + val icon = getIconForStep(step) return when (step) { SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> StepContent( title = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_title, + R.string.expert_mode_setup_wizard_enable_accessibility_service_title, ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_description, + R.string.expert_mode_setup_wizard_enable_accessibility_service_description, ), - icon = Icons.Rounded.Accessibility, + icon = icon, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_accessibility_service_button, + R.string.expert_mode_setup_wizard_enable_accessibility_service_button, ), ) SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> StepContent( title = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_title, + R.string.expert_mode_setup_wizard_enable_notification_permission_title, ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_description, + R.string.expert_mode_setup_wizard_enable_notification_permission_description, ), - icon = Icons.Rounded.Notifications, + icon = icon, buttonText = stringResource( - R.string.pro_mode_setup_wizard_enable_notification_permission_button, + R.string.expert_mode_setup_wizard_enable_notification_permission_button, ), ) SystemBridgeSetupStep.DEVELOPER_OPTIONS -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_enable_developer_options_title), + title = stringResource( + R.string.expert_mode_setup_wizard_enable_developer_options_title, + ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_developer_options_description, + R.string.expert_mode_setup_wizard_enable_developer_options_description, ), - icon = Icons.Rounded.Build, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + icon = icon, + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.WIFI_NETWORK -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_connect_wifi_title), - message = stringResource(R.string.pro_mode_setup_wizard_connect_wifi_description), - icon = KeyMapperIcons.SignalWifiNotConnected, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + title = stringResource(R.string.expert_mode_setup_wizard_connect_wifi_title), + message = stringResource(R.string.expert_mode_setup_wizard_connect_wifi_description), + icon = icon, + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.WIRELESS_DEBUGGING -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_enable_wireless_debugging_title), + title = stringResource( + R.string.expert_mode_setup_wizard_enable_wireless_debugging_title, + ), message = stringResource( - R.string.pro_mode_setup_wizard_enable_wireless_debugging_description, + R.string.expert_mode_setup_wizard_enable_wireless_debugging_description, ), - icon = Icons.Rounded.BugReport, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + icon = icon, + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.ADB_PAIRING -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_pair_wireless_debugging_title), + title = stringResource(R.string.expert_mode_setup_wizard_pair_wireless_debugging_title), message = stringResource( - R.string.pro_mode_setup_wizard_pair_wireless_debugging_description, + R.string.expert_mode_setup_wizard_pair_wireless_debugging_description, ), - icon = Icons.Rounded.Link, - buttonText = stringResource(R.string.pro_mode_setup_wizard_go_to_settings_button), + icon = icon, + buttonText = stringResource(R.string.expert_mode_setup_wizard_go_to_settings_button), ) SystemBridgeSetupStep.START_SERVICE -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_start_service_title), - message = stringResource(R.string.pro_mode_setup_wizard_start_service_description), - icon = Icons.Rounded.PlayArrow, - buttonText = stringResource(R.string.pro_mode_root_detected_button_start_service), + title = stringResource(R.string.expert_mode_setup_wizard_start_service_title), + message = stringResource(R.string.expert_mode_setup_wizard_start_service_description), + icon = icon, + buttonText = stringResource(R.string.expert_mode_root_detected_button_start_service), ) SystemBridgeSetupStep.STARTED -> StepContent( - title = stringResource(R.string.pro_mode_setup_wizard_complete_title), - message = stringResource(R.string.pro_mode_setup_wizard_complete_text), - icon = Icons.Rounded.CheckCircleOutline, - buttonText = stringResource(R.string.pro_mode_setup_wizard_complete_button), + title = stringResource(R.string.expert_mode_setup_wizard_complete_title), + message = stringResource(R.string.expert_mode_setup_wizard_complete_text), + icon = icon, + buttonText = stringResource(R.string.expert_mode_setup_wizard_complete_button), ) } } -private data class StepContent( - val title: String, - val message: String, - val icon: ImageVector, - val buttonText: String, -) - @Preview(name = "Accessibility Service Step") @Composable -private fun ProModeSetupScreenAccessibilityServicePreview() { +private fun ExpertModeSetupScreenAccessibilityServicePreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.ACCESSIBILITY_SERVICE + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 1, stepCount = 6, - step = SystemBridgeSetupStep.ACCESSIBILITY_SERVICE, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = false, + isStarting = false, ), ), ) @@ -421,16 +465,19 @@ private fun ProModeSetupScreenAccessibilityServicePreview() { @Preview(name = "Notification Permission Step") @Composable -private fun ProModeSetupScreenNotificationPermissionPreview() { +private fun ExpertModeSetupScreenNotificationPermissionPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.NOTIFICATION_PERMISSION + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 2, stepCount = 6, - step = SystemBridgeSetupStep.NOTIFICATION_PERMISSION, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -439,16 +486,19 @@ private fun ProModeSetupScreenNotificationPermissionPreview() { @Preview(name = "Developer Options Step") @Composable -private fun ProModeSetupScreenDeveloperOptionsPreview() { +private fun ExpertModeSetupScreenDeveloperOptionsPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.DEVELOPER_OPTIONS + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 2, stepCount = 6, - step = SystemBridgeSetupStep.DEVELOPER_OPTIONS, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -457,16 +507,19 @@ private fun ProModeSetupScreenDeveloperOptionsPreview() { @Preview(name = "WiFi Network Step") @Composable -private fun ProModeSetupScreenWifiNetworkPreview() { +private fun ExpertModeSetupScreenWifiNetworkPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.WIFI_NETWORK + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 3, stepCount = 6, - step = SystemBridgeSetupStep.WIFI_NETWORK, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -475,16 +528,19 @@ private fun ProModeSetupScreenWifiNetworkPreview() { @Preview(name = "Wireless Debugging Step") @Composable -private fun ProModeSetupScreenWirelessDebuggingPreview() { +private fun ExpertModeSetupScreenWirelessDebuggingPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.WIRELESS_DEBUGGING + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 4, stepCount = 6, - step = SystemBridgeSetupStep.WIRELESS_DEBUGGING, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = false, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -493,16 +549,19 @@ private fun ProModeSetupScreenWirelessDebuggingPreview() { @Preview(name = "ADB Pairing Step", widthDp = 400, heightDp = 400) @Composable -private fun ProModeSetupScreenAdbPairingPreview() { +private fun ExpertModeSetupScreenAdbPairingPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.ADB_PAIRING + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 5, stepCount = 6, - step = SystemBridgeSetupStep.ADB_PAIRING, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -511,16 +570,19 @@ private fun ProModeSetupScreenAdbPairingPreview() { @Preview(name = "Start Service Step", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenStartServicePreview() { +private fun ExpertModeSetupScreenStartServicePreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.START_SERVICE + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 6, stepCount = 6, - step = SystemBridgeSetupStep.START_SERVICE, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -529,16 +591,19 @@ private fun ProModeSetupScreenStartServicePreview() { @Preview(name = "Started", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenStartedPreview() { +private fun ExpertModeSetupScreenStartedPreview() { KeyMapperTheme { - ProModeSetupScreen( + val step = SystemBridgeSetupStep.STARTED + ExpertModeSetupScreen( state = State.Data( - ProModeSetupState( + ExpertModeSetupState( stepNumber = 8, stepCount = 8, - step = SystemBridgeSetupStep.STARTED, + step = step, + stepContent = createPreviewStepContent(step), isSetupAssistantChecked = true, isSetupAssistantButtonEnabled = true, + isStarting = false, ), ), ) @@ -547,9 +612,9 @@ private fun ProModeSetupScreenStartedPreview() { @Preview(name = "Loading", uiMode = Configuration.UI_MODE_NIGHT_YES) @Composable -private fun ProModeSetupScreenLoadingPreview() { +private fun ExpertModeSetupScreenLoadingPreview() { KeyMapperTheme { - ProModeSetupScreen( + ExpertModeSetupScreen( state = State.Loading, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt new file mode 100644 index 0000000000..f920a4aad3 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupState.kt @@ -0,0 +1,13 @@ +package io.github.sds100.keymapper.base.expertmode + +import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep + +data class ExpertModeSetupState( + val stepNumber: Int, + val stepCount: Int, + val step: SystemBridgeSetupStep, + val stepContent: StepContent, + val isSetupAssistantChecked: Boolean, + val isSetupAssistantButtonEnabled: Boolean, + val isStarting: Boolean, +) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt new file mode 100644 index 0000000000..bf204bdb93 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeSetupViewModel.kt @@ -0,0 +1,26 @@ +package io.github.sds100.keymapper.base.expertmode + +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.ui.ResourceProvider +import javax.inject.Inject +import kotlinx.coroutines.launch + +@HiltViewModel +class ExpertModeSetupViewModel @Inject constructor( + delegate: SystemBridgeSetupDelegate, + navigationProvider: NavigationProvider, + resourceProvider: ResourceProvider, +) : ViewModel(), + SystemBridgeSetupDelegate by delegate, + NavigationProvider by navigationProvider, + ResourceProvider by resourceProvider { + + fun onBackClick() { + viewModelScope.launch { + popBackStack() + } + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt similarity index 81% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt index 415b7163f0..87dfc97116 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ExpertModeViewModel.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableStateOf @@ -28,7 +28,7 @@ import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @HiltViewModel -class ProModeViewModel @Inject constructor( +class ExpertModeViewModel @Inject constructor( private val useCase: SystemBridgeSetupUseCase, resourceProvider: ResourceProvider, dialogProvider: DialogProvider, @@ -43,23 +43,24 @@ class ProModeViewModel @Inject constructor( } @OptIn(ExperimentalCoroutinesApi::class) - val warningState: StateFlow = + val warningState: StateFlow = useCase.isWarningUnderstood .flatMapLatest { isUnderstood -> createWarningStateFlow(isUnderstood) } .stateIn( viewModelScope, SharingStarted.Eagerly, - ProModeWarningState.CountingDown( + ExpertModeWarningState.CountingDown( WARNING_COUNT_DOWN_SECONDS, ), ) - val setupState: StateFlow> = + val setupState: StateFlow> = combine( useCase.isSystemBridgeConnected, useCase.isRootGranted, useCase.shizukuSetupState, useCase.isNotificationPermissionGranted, + useCase.isSystemBridgeStarting, ::buildSetupState, ).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading) @@ -81,17 +82,17 @@ class ProModeViewModel @Inject constructor( showInfoCard = true } - private fun createWarningStateFlow(isUnderstood: Boolean): Flow = + private fun createWarningStateFlow(isUnderstood: Boolean): Flow = if (isUnderstood) { - flowOf(ProModeWarningState.Understood) + flowOf(ExpertModeWarningState.Understood) } else { flow { repeat(WARNING_COUNT_DOWN_SECONDS) { - emit(ProModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS - it)) + emit(ExpertModeWarningState.CountingDown(WARNING_COUNT_DOWN_SECONDS - it)) delay(1000L) } - emit(ProModeWarningState.Idle) + emit(ExpertModeWarningState.Idle) } } @@ -138,7 +139,7 @@ class ProModeViewModel @Inject constructor( fun onSetupWithKeyMapperClick() { viewModelScope.launch { - navigate("setup_pro_mode_with_key_mapper", NavDestination.ProModeSetup) + navigate("setup_expert_mode_with_key_mapper", NavDestination.ExpertModeSetup) } } @@ -155,38 +156,41 @@ class ProModeViewModel @Inject constructor( isRootGranted: Boolean, shizukuSetupState: ShizukuSetupState, isNotificationPermissionGranted: Boolean, - ): State { + isSystemBridgeStarting: Boolean, + ): State { if (isSystemBridgeConnected) { return State.Data( - ProModeState.Started( + ExpertModeState.Started( isDefaultUsbModeCompatible = useCase.isCompatibleUsbModeSelected().valueOrNull() ?: false, ), ) } else { return State.Data( - ProModeState.Stopped( + ExpertModeState.Stopped( isRootGranted = isRootGranted, shizukuSetupState = shizukuSetupState, isNotificationPermissionGranted = isNotificationPermissionGranted, + isStarting = isSystemBridgeStarting, ), ) } } } -sealed class ProModeWarningState { - data class CountingDown(val seconds: Int) : ProModeWarningState() - data object Idle : ProModeWarningState() - data object Understood : ProModeWarningState() +sealed class ExpertModeWarningState { + data class CountingDown(val seconds: Int) : ExpertModeWarningState() + data object Idle : ExpertModeWarningState() + data object Understood : ExpertModeWarningState() } -sealed class ProModeState { +sealed class ExpertModeState { data class Stopped( val isRootGranted: Boolean, val shizukuSetupState: ShizukuSetupState, val isNotificationPermissionGranted: Boolean, - ) : ProModeState() + val isStarting: Boolean, + ) : ExpertModeState() - data class Started(val isDefaultUsbModeCompatible: Boolean) : ProModeState() + data class Started(val isDefaultUsbModeCompatible: Boolean) : ExpertModeState() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt similarity index 66% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt index 1c9814a519..2f06044bc0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ShizukuSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/ShizukuSetupState.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode enum class ShizukuSetupState { NOT_FOUND, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt new file mode 100644 index 0000000000..8aac069b9b --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/StepContent.kt @@ -0,0 +1,10 @@ +package io.github.sds100.keymapper.base.expertmode + +import androidx.compose.ui.graphics.vector.ImageVector + +data class StepContent( + val title: String, + val message: String, + val icon: ImageVector, + val buttonText: String, +) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt similarity index 58% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt index ca660a171c..cb69b61f41 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeAutoStarter.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarter.kt @@ -1,20 +1,22 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode +import android.annotation.SuppressLint import android.os.Build -import android.os.SystemClock import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import io.github.sds100.keymapper.base.BaseMainActivity +import io.github.sds100.keymapper.base.BuildConfig import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.system.notifications.NotificationController.Companion.CHANNEL_SETUP_ASSISTANT import io.github.sds100.keymapper.base.system.notifications.NotificationController.Companion.ID_SYSTEM_BRIDGE_STATUS import io.github.sds100.keymapper.base.utils.ui.ResourceProvider +import io.github.sds100.keymapper.common.BuildConfigProvider import io.github.sds100.keymapper.common.notifications.KMNotificationAction +import io.github.sds100.keymapper.common.utils.Clock import io.github.sds100.keymapper.common.utils.Constants 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.sysbridge.BuildConfig import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState import io.github.sds100.keymapper.sysbridge.manager.isConnected @@ -44,6 +46,7 @@ import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.map import kotlinx.coroutines.launch import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber /** @@ -54,6 +57,8 @@ import timber.log.Timber @Singleton class SystemBridgeAutoStarter @Inject constructor( private val coroutineScope: CoroutineScope, + private val buildConfig: BuildConfigProvider, + private val clock: Clock, private val suAdapter: SuAdapter, private val shizukuAdapter: ShizukuAdapter, private val connectionManager: SystemBridgeConnectionManager, @@ -71,101 +76,107 @@ class SystemBridgeAutoStarter @Inject constructor( } // Use flatMapLatest so that any calls to ADB are only done if strictly necessary. + @SuppressLint("NewApi") @OptIn(ExperimentalCoroutinesApi::class) private val autoStartTypeFlow: Flow = - suAdapter.isRootGranted.flatMapLatest { isRooted -> - if (isRooted) { - flowOf(AutoStartType.ROOT) - } else { - val useShizukuFlow = - combine( - shizukuAdapter.isStarted, - permissionAdapter.isGrantedFlow(Permission.SHIZUKU), - ) { isStarted, isGranted -> - isStarted && isGranted - } - - useShizukuFlow.flatMapLatest { useShizuku -> - if (useShizuku) { - flowOf(AutoStartType.SHIZUKU) - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - val isAdbAutoStartAllowed = combine( - permissionAdapter.isGrantedFlow(Permission.WRITE_SECURE_SETTINGS), - networkAdapter.isWifiConnected, - ) { isWriteSecureSettingsGranted, isWifiConnected -> - isWriteSecureSettingsGranted && - isWifiConnected && - setupController.isAdbPaired() + suAdapter.isRootGranted + .filterNotNull() + .flatMapLatest { isRooted -> + if (isRooted) { + flowOf(AutoStartType.ROOT) + } else { + val useShizukuFlow = + combine( + shizukuAdapter.isStarted, + permissionAdapter.isGrantedFlow(Permission.SHIZUKU), + ) { isStarted, isGranted -> + isStarted && isGranted } - isAdbAutoStartAllowed.distinctUntilChanged() - .map { isAdbAutoStartAllowed -> - if (isAdbAutoStartAllowed) AutoStartType.ADB else null - }.filterNotNull() - } else { - flowOf(null) + useShizukuFlow.flatMapLatest { useShizuku -> + if (useShizuku) { + flowOf(AutoStartType.SHIZUKU) + } else if (buildConfig.sdkInt >= Build.VERSION_CODES.R) { + val isAdbAutoStartAllowed = combine( + permissionAdapter.isGrantedFlow(Permission.WRITE_SECURE_SETTINGS), + networkAdapter.isWifiConnected, + ) { isWriteSecureSettingsGranted, isWifiConnected -> + isWriteSecureSettingsGranted && + isWifiConnected && + setupController.isAdbPaired() + } + + isAdbAutoStartAllowed.distinctUntilChanged() + .map { isAdbAutoStartAllowed -> + if (isAdbAutoStartAllowed) { + AutoStartType.ADB + } else { + null + } + }.filterNotNull() + } else { + flowOf(null) + } } } } - } /** * This emits values when the system bridge needs restarting after it being killed. */ @OptIn(ExperimentalCoroutinesApi::class) - private val restartFlow: Flow = + private val autoStartFlow: Flow = connectionManager.connectionState.flatMapLatest { connectionState -> // Do not autostart if it is connected or it was killed from the user if (connectionState !is SystemBridgeConnectionState.Disconnected || - connectionState.isExpected + connectionState.isStoppedByUser || + !getIsUsedBefore() || + getIsStoppedByUser() || + isSystemBridgeEmergencyKilled() || + !isAutoStartEnabled() ) { flowOf(null) - } else { + } else if (isWithinAutoStartCooldown()) { // Do not autostart if the system bridge was killed shortly after. // This prevents infinite loops happening. - if (lastAutoStartTime != null && - connectionState.time - lastAutoStartTime!! < 30000 - ) { - Timber.w( - "Not auto starting the system bridge because it was last auto started less than 30 secs ago", - ) - showSystemBridgeKilledNotification( - getString(R.string.system_bridge_died_notification_not_restarting_text), - ) - flowOf(null) - } else { - autoStartTypeFlow - } + Timber.w( + "Not auto starting the system bridge because it was last auto started less than 5 mins ago", + ) + showSystemBridgeKilledNotification( + getString(R.string.system_bridge_died_notification_not_restarting_text), + ) + flowOf(null) + } else { + autoStartTypeFlow } } - private var lastAutoStartTime: Long? = null - /** * This must only be called once in the application lifecycle */ - @OptIn(FlowPreview::class) + @OptIn(FlowPreview::class, ExperimentalCoroutinesApi::class) fun init() { coroutineScope.launch { - // The Key Mapper process may not necessarily be started on boot due to the - // on boot receiver so assume if it is started within a minute of boot that - // it should be auto started. - val isBoot = SystemClock.uptimeMillis() < 60000 - - if (isBoot) { - handleAutoStartOnBoot() - } else if (BuildConfig.DEBUG && connectionManager.isConnected()) { + Timber.i( + "SystemBridgeAutoStarter init: time since boot=${clock.elapsedRealtime() / 1000} seconds", + ) + + if (BuildConfig.DEBUG && connectionManager.isConnected()) { + delay(1000) // This is useful when developing and need to restart the system bridge // after making changes to it. Timber.w("Restarting system bridge on debug build.") connectionManager.restartSystemBridge() - } else { - handleAutoStartFromPreVersion4() } - // Only start collecting the restart flow after potentially auto starting it for the first time. - restartFlow + // Wait 5 seconds for the system bridge to potentially connect itself to Key Mapper + // before deciding whether to start it. + delay(5000) + + handleAutoStartFromPreVersion4() + + autoStartFlow .distinctUntilChanged() // Must come before the filterNotNull .filterNotNull() .collectLatest { type -> @@ -174,74 +185,59 @@ class SystemBridgeAutoStarter @Inject constructor( } } - private suspend fun handleAutoStartOnBoot() { - // Do not autostart if the device was force rebooted. This may be a sign that PRO mode - // was broken and the user was trying to reset it. - val isCleanShutdown = preferences.get(Keys.isCleanShutdown).map { it ?: false }.first() - - Timber.i( - "SystemBridgeAutoStarter init: isBoot=true, isCleanShutdown=$isCleanShutdown", - ) - - // Reset the value after reading it. - preferences.set(Keys.isCleanShutdown, false) - - val isBootAutoStartEnabled = preferences.get(Keys.isProModeAutoStartBootEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_AUTOSTART_BOOT } - .first() - - // Wait 5 seconds for the system bridge to potentially connect itself to Key Mapper - // before starting it. - delay(5000) - - val connectionState = connectionManager.connectionState.value - - if (isCleanShutdown && - isBootAutoStartEnabled && - connectionState !is SystemBridgeConnectionState.Connected - ) { - val autoStartType = autoStartTypeFlow.first() + private suspend fun handleAutoStartFromPreVersion4() { + @Suppress("DEPRECATION") + val upgradedFromPreVersion4 = + preferences.get(Keys.handledUpgradeToExpertMode).first() == null - if (autoStartType != null) { - autoStart(autoStartType) - } + if (!upgradedFromPreVersion4) { + return } - } - private suspend fun handleAutoStartFromPreVersion4() { - val isFirstTime = preferences.get(Keys.handledRootToProModeUpgrade).first() == null + val isRooted: Boolean = withTimeoutOrNull(1000) { + suAdapter.isRootGranted.filterNotNull().first() + } ?: false - if (isFirstTime && suAdapter.isRootGranted.value) { + if (isRooted) { Timber.i( "Auto starting system bridge because upgraded from pre version 4.0 and was rooted", ) autoStart(AutoStartType.ROOT) - preferences.set(Keys.handledRootToProModeUpgrade, true) + preferences.set(Keys.handledUpgradeToExpertMode, true) + preferences.set(Keys.keyEventActionsUseSystemBridge, true) + return } - } - private suspend fun autoStart(type: AutoStartType) { - if (isSystemBridgeEmergencyKilled()) { - Timber.w( - "Not auto starting the system bridge because it was emergency killed by the user", + // Try Shizuku after the root check because root is more reliable. + val isShizukuStarted: Boolean = shizukuAdapter.isStarted.value + + if (isShizukuStarted) { + Timber.i( + "Auto starting system bridge because upgraded from pre version 4.0 and Shizuku was started", ) + + autoStart(AutoStartType.SHIZUKU) + preferences.set(Keys.handledUpgradeToExpertMode, true) + preferences.set(Keys.keyEventActionsUseSystemBridge, true) return } + } + private suspend fun autoStart(type: AutoStartType) { if (connectionManager.isConnected()) { Timber.i("Not auto starting with $type because already connected.") return } - lastAutoStartTime = SystemClock.elapsedRealtime() + preferences.set(Keys.systemBridgeLastAutoStartTime, clock.elapsedRealtime()) when (type) { AutoStartType.ADB -> { Timber.i("Auto starting system bridge with ADB") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_adb_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_adb_text, ), ) @@ -252,7 +248,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with Shizuku") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_shizuku_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_shizuku, ), ) connectionManager.startWithShizuku() @@ -262,7 +258,7 @@ class SystemBridgeAutoStarter @Inject constructor( Timber.i("Auto starting system bridge with root") showAutoStartNotification( getString( - R.string.pro_mode_setup_notification_auto_start_system_bridge_root_text, + R.string.expert_mode_setup_notification_auto_start_system_bridge_root_text, ), ) connectionManager.startWithRoot() @@ -281,17 +277,41 @@ class SystemBridgeAutoStarter @Inject constructor( } } + private suspend fun getIsUsedBefore(): Boolean { + return preferences.get(Keys.isSystemBridgeUsed).first() ?: false + } + + private suspend fun getIsStoppedByUser(): Boolean { + return preferences.get(Keys.isSystemBridgeStoppedByUser).first() ?: false + } + private suspend fun isSystemBridgeEmergencyKilled(): Boolean { return preferences.get(Keys.isSystemBridgeEmergencyKilled).first() == true } + /** + * Whether the system bridge died less than 5 minutes after the previous time it was + * auto started. + */ + private suspend fun isWithinAutoStartCooldown(): Boolean { + val lastAutoStartTime = preferences.get(Keys.systemBridgeLastAutoStartTime).first() + return lastAutoStartTime != null && + clock.elapsedRealtime() - lastAutoStartTime < (5 * 60_000) + } + + private suspend fun isAutoStartEnabled(): Boolean { + return preferences.get(Keys.isSystemBridgeKeepAliveEnabled) + .map { it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE } + .first() + } + private fun showSystemBridgeKilledNotification(text: String) { val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, channel = CHANNEL_SETUP_ASSISTANT, title = getString(R.string.system_bridge_died_notification_title), text = text, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, showOnLockscreen = true, onGoing = false, priority = NotificationCompat.PRIORITY_MAX, @@ -307,10 +327,12 @@ class SystemBridgeAutoStarter @Inject constructor( private fun showAutoStartNotification(text: String) { val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, - title = getString(R.string.pro_mode_setup_notification_auto_start_system_bridge_title), + title = getString( + R.string.expert_mode_setup_notification_auto_start_system_bridge_title, + ), text = text, channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, priority = NotificationCompat.PRIORITY_MAX, onGoing = true, showIndeterminateProgress = true, @@ -324,11 +346,13 @@ class SystemBridgeAutoStarter @Inject constructor( val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, title = getString( - R.string.pro_mode_setup_notification_start_system_bridge_failed_title, + R.string.expert_mode_setup_notification_start_system_bridge_failed_title, + ), + text = getString( + R.string.expert_mode_setup_notification_start_system_bridge_failed_text, ), - text = getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_text), channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt similarity index 85% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt index 464524777f..0589a80761 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupAssistantController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupAssistantController.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.app.ActivityManager import android.os.Build @@ -25,23 +25,20 @@ 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.sysbridge.manager.SystemBridgeConnectionManager -import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import io.github.sds100.keymapper.sysbridge.manager.awaitConnected import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupController import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep import io.github.sds100.keymapper.system.notifications.NotificationModel import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Job -import kotlinx.coroutines.TimeoutCancellationException import kotlinx.coroutines.delay import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch -import kotlinx.coroutines.withTimeout +import kotlinx.coroutines.withTimeoutOrNull import timber.log.Timber @Suppress("KotlinConstantConditions") @@ -88,12 +85,12 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( private val activityManager: ActivityManager = accessibilityService.getSystemService()!! private val isInteractive: StateFlow = - preferenceRepository.get(Keys.isProModeInteractiveSetupAssistantEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT } + preferenceRepository.get(Keys.isExpertModeInteractiveSetupAssistantEnabled) + .map { it ?: PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } .stateIn( coroutineScope, SharingStarted.Eagerly, - PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT, + PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT, ) private var interactionStep: InteractionStep? = null @@ -206,11 +203,13 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( stopInteracting() showNotification( - getString(R.string.pro_mode_setup_notification_invalid_pairing_code_title), - getString(R.string.pro_mode_setup_notification_invalid_pairing_code_text), + getString(R.string.expert_mode_setup_notification_invalid_pairing_code_title), + getString(R.string.expert_mode_setup_notification_invalid_pairing_code_text), actions = listOf( KMNotificationAction.RemoteInput.PairingCode to - getString(R.string.pro_mode_setup_notification_action_input_pairing_code), + getString( + R.string.expert_mode_setup_notification_action_input_pairing_code, + ), ), ) } @@ -219,17 +218,9 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( private suspend fun onPairingSuccess() { setupController.startWithAdb() - val isStarted = try { - withTimeout(10000L) { - systemBridgeConnectionManager.connectionState - .filterIsInstance() - .first() - } - - true - } catch (_: TimeoutCancellationException) { - false - } + val isStarted = withTimeoutOrNull(10000L) { + systemBridgeConnectionManager.awaitConnected() + } != null if (isStarted) { Timber.i("System bridge started after pairing. Going back to Key Mapper.") @@ -237,8 +228,8 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( } else { Timber.e("Failed to start system bridge after pairing.") showNotification( - getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_title), - getString(R.string.pro_mode_setup_notification_start_system_bridge_failed_text), + getString(R.string.expert_mode_setup_notification_start_system_bridge_failed_title), + getString(R.string.expert_mode_setup_notification_start_system_bridge_failed_text), onClickAction = KMNotificationAction.Activity.MainActivity( BaseMainActivity.ACTION_START_SYSTEM_BRIDGE, ), @@ -281,7 +272,7 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( channel = NotificationController.Companion.CHANNEL_SETUP_ASSISTANT, title = title, text = text, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, @@ -309,15 +300,15 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( when (step) { SystemBridgeSetupStep.DEVELOPER_OPTIONS -> { showNotification( - getString(R.string.pro_mode_setup_notification_tap_build_number_title), - getString(R.string.pro_mode_setup_notification_tap_build_number_text), + getString(R.string.expert_mode_setup_notification_tap_build_number_title), + getString(R.string.expert_mode_setup_notification_tap_build_number_text), ) } SystemBridgeSetupStep.ADB_PAIRING -> { showNotification( - getString(R.string.pro_mode_setup_notification_pairing_title), - getString(R.string.pro_mode_setup_notification_pairing_text), + getString(R.string.expert_mode_setup_notification_pairing_title), + getString(R.string.expert_mode_setup_notification_pairing_text), ) interactionStep = InteractionStep.PAIR_DEVICE @@ -339,15 +330,15 @@ class SystemBridgeSetupAssistantController @AssistedInject constructor( showNotification( title = getString( - R.string.pro_mode_setup_notification_pairing_button_not_found_title, + R.string.expert_mode_setup_notification_pairing_button_not_found_title, ), text = getString( - R.string.pro_mode_setup_notification_pairing_button_not_found_text, + R.string.expert_mode_setup_notification_pairing_button_not_found_text, ), actions = listOf( KMNotificationAction.RemoteInput.PairingCode to getString( - R.string.pro_mode_setup_notification_action_input_pairing_code, + R.string.expert_mode_setup_notification_action_input_pairing_code, ), ), ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt new file mode 100644 index 0000000000..c4ba397200 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupDelegate.kt @@ -0,0 +1,209 @@ +package io.github.sds100.keymapper.base.expertmode + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.rounded.Accessibility +import androidx.compose.material.icons.rounded.BugReport +import androidx.compose.material.icons.rounded.Build +import androidx.compose.material.icons.rounded.CheckCircleOutline +import androidx.compose.material.icons.rounded.Link +import androidx.compose.material.icons.rounded.Notifications +import androidx.compose.material.icons.rounded.PlayArrow +import io.github.sds100.keymapper.base.R +import io.github.sds100.keymapper.base.utils.ui.ResourceProvider +import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons +import io.github.sds100.keymapper.base.utils.ui.compose.icons.SignalWifiNotConnected +import io.github.sds100.keymapper.common.utils.State +import io.github.sds100.keymapper.common.utils.dataOrNull +import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.combine +import kotlinx.coroutines.flow.stateIn + +abstract class SystemBridgeSetupDelegateImpl( + val viewModelScope: CoroutineScope, + private val useCase: SystemBridgeSetupUseCase, + private val resourceProvider: ResourceProvider, +) : SystemBridgeSetupDelegate, + ResourceProvider by resourceProvider { + override val setupState: StateFlow> = + combine( + useCase.nextSetupStep, + useCase.isSetupAssistantEnabled, + useCase.isSystemBridgeStarting, + ::buildState, + ).stateIn( + viewModelScope, + SharingStarted.Eagerly, + State.Loading, + ) + + override fun onSetupStepButtonClick() { + // Do not check the latest value in the use case because there is significant latency + // when it is checking whether it is paired + val currentStep = setupState.value.dataOrNull()?.step ?: return + + when (currentStep) { + SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> useCase.enableAccessibilityService() + SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> useCase.requestNotificationPermission() + SystemBridgeSetupStep.DEVELOPER_OPTIONS -> useCase.enableDeveloperOptions() + SystemBridgeSetupStep.WIFI_NETWORK -> useCase.connectWifiNetwork() + SystemBridgeSetupStep.WIRELESS_DEBUGGING -> useCase.enableWirelessDebugging() + SystemBridgeSetupStep.ADB_PAIRING -> useCase.pairWirelessAdb() + SystemBridgeSetupStep.START_SERVICE -> useCase.startSystemBridgeWithAdb() + SystemBridgeSetupStep.STARTED -> onFinishClick() + } + } + + abstract fun onFinishClick() + + override fun onSetupAssistantClick() { + useCase.toggleSetupAssistant() + } + + override fun getStepContent(step: SystemBridgeSetupStep): StepContent { + return when (step) { + SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_enable_accessibility_service_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_enable_accessibility_service_description, + ), + icon = Icons.Rounded.Accessibility, + buttonText = getString( + R.string.expert_mode_setup_wizard_enable_accessibility_service_button, + ), + ) + + SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_enable_notification_permission_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_enable_notification_permission_description, + ), + icon = Icons.Rounded.Notifications, + buttonText = getString( + R.string.expert_mode_setup_wizard_enable_notification_permission_button, + ), + ) + + SystemBridgeSetupStep.DEVELOPER_OPTIONS -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_enable_developer_options_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_enable_developer_options_description, + ), + icon = Icons.Rounded.Build, + buttonText = getString( + R.string.expert_mode_setup_wizard_go_to_settings_button, + ), + ) + + SystemBridgeSetupStep.WIFI_NETWORK -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_connect_wifi_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_connect_wifi_description, + ), + icon = KeyMapperIcons.SignalWifiNotConnected, + buttonText = getString( + R.string.expert_mode_setup_wizard_go_to_settings_button, + ), + ) + + SystemBridgeSetupStep.WIRELESS_DEBUGGING -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_enable_wireless_debugging_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_enable_wireless_debugging_description, + ), + icon = Icons.Rounded.BugReport, + buttonText = getString( + R.string.expert_mode_setup_wizard_go_to_settings_button, + ), + ) + + SystemBridgeSetupStep.ADB_PAIRING -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_pair_wireless_debugging_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_pair_wireless_debugging_description, + ), + icon = Icons.Rounded.Link, + buttonText = getString( + R.string.expert_mode_setup_wizard_go_to_settings_button, + ), + ) + + SystemBridgeSetupStep.START_SERVICE -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_start_service_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_start_service_description, + ), + icon = Icons.Rounded.PlayArrow, + buttonText = getString( + R.string.expert_mode_root_detected_button_start_service, + ), + ) + + SystemBridgeSetupStep.STARTED -> StepContent( + title = getString( + R.string.expert_mode_setup_wizard_complete_title, + ), + message = getString( + R.string.expert_mode_setup_wizard_complete_text, + ), + icon = Icons.Rounded.CheckCircleOutline, + buttonText = getString( + R.string.expert_mode_setup_wizard_complete_button, + ), + ) + } + } + + private fun buildState( + step: SystemBridgeSetupStep, + isSetupAssistantUserEnabled: Boolean, + isStarting: Boolean, + ): State.Data { + // Uncheck the setup assistant if the accessibility service is disabled since it is + // required for the setup assistant to work + val isSetupAssistantChecked = if (step == SystemBridgeSetupStep.ACCESSIBILITY_SERVICE) { + false + } else { + isSetupAssistantUserEnabled + } + + val stepContent = getStepContent(step) + + return State.Data( + ExpertModeSetupState( + stepNumber = step.stepIndex + 1, + stepCount = SystemBridgeSetupStep.entries.size, + step = step, + stepContent = stepContent, + isSetupAssistantChecked = isSetupAssistantChecked, + isSetupAssistantButtonEnabled = + step != SystemBridgeSetupStep.ACCESSIBILITY_SERVICE && + step != SystemBridgeSetupStep.STARTED, + isStarting = isStarting, + ), + ) + } +} + +interface SystemBridgeSetupDelegate { + val setupState: StateFlow> + fun onSetupStepButtonClick() + fun onSetupAssistantClick() + fun getStepContent(step: SystemBridgeSetupStep): StepContent +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt similarity index 83% rename from base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt rename to base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt index 570da2409a..c8ea53a633 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/SystemBridgeSetupUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCase.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.promode +package io.github.sds100.keymapper.base.expertmode import android.os.Build import android.os.Process @@ -26,7 +26,6 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOn @@ -54,7 +53,7 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override val isWarningUnderstood: Flow = - preferences.get(Keys.isProModeWarningUnderstood).map { it ?: false } + preferences.get(Keys.isExpertModeWarningUnderstood).map { it ?: false } private val isAdbAutoStartAllowed: Flow = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { @@ -71,18 +70,18 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override fun onUnderstoodWarning() { - preferences.set(Keys.isProModeWarningUnderstood, true) + preferences.set(Keys.isExpertModeWarningUnderstood, true) } override val isSetupAssistantEnabled: Flow = - preferences.get(Keys.isProModeInteractiveSetupAssistantEnabled).map { - it ?: PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT + preferences.get(Keys.isExpertModeInteractiveSetupAssistantEnabled).map { + it ?: PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } override fun toggleSetupAssistant() { - preferences.update(Keys.isProModeInteractiveSetupAssistantEnabled) { + preferences.update(Keys.isExpertModeInteractiveSetupAssistantEnabled) { if (it == null) { - !PreferenceDefaults.PRO_MODE_INTERACTIVE_SETUP_ASSISTANT + !PreferenceDefaults.EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT } else { !it } @@ -93,6 +92,9 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( systemBridgeConnectionManager.connectionState .map { it is SystemBridgeConnectionState.Connected } + override val isSystemBridgeStarting: Flow = + systemBridgeSetupController.isStarting + override val isNotificationPermissionGranted: Flow = permissionAdapter.isGrantedFlow(Permission.POST_NOTIFICATIONS) @@ -120,7 +122,7 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } } - override val isRootGranted: Flow = suAdapter.isRootGranted + override val isRootGranted: Flow = suAdapter.isRootGranted.map { it ?: false } override val shizukuSetupState: Flow = combine( shizukuAdapter.isInstalled, @@ -148,6 +150,10 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( } override fun stopSystemBridge() { + // Save that they've stopped the system bridge so when the app process launches again + // it will set the isStoppedByUser to true. + preferences.set(Keys.isSystemBridgeStoppedByUser, true) + systemBridgeConnectionManager.stopSystemBridge() } @@ -174,38 +180,43 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( override fun startSystemBridgeWithRoot() { preferences.set(Keys.isSystemBridgeEmergencyKilled, false) + preferences.set(Keys.isSystemBridgeStoppedByUser, false) systemBridgeSetupController.startWithRoot() } override fun startSystemBridgeWithShizuku() { preferences.set(Keys.isSystemBridgeEmergencyKilled, false) + preferences.set(Keys.isSystemBridgeStoppedByUser, false) systemBridgeSetupController.startWithShizuku() } - override suspend fun startSystemBridgeWithAdb() { + override fun startSystemBridgeWithAdb() { preferences.set(Keys.isSystemBridgeEmergencyKilled, false) - if (isAdbAutoStartAllowed.first()) { - systemBridgeSetupController.autoStartWithAdb() - } else { - systemBridgeSetupController.startWithAdb() - } + preferences.set(Keys.isSystemBridgeStoppedByUser, false) + systemBridgeSetupController.startWithAdb() + } + + override fun autoStartSystemBridgeWithAdb() { + preferences.set(Keys.isSystemBridgeEmergencyKilled, false) + preferences.set(Keys.isSystemBridgeStoppedByUser, false) + systemBridgeSetupController.autoStartWithAdb() } override fun isInfoDismissed(): Boolean { - return preferences.get(Keys.isProModeInfoDismissed).map { it ?: false }.firstBlocking() + return preferences.get(Keys.isExpertModeInfoDismissed).map { it ?: false }.firstBlocking() } override fun dismissInfo() { - preferences.set(Keys.isProModeInfoDismissed, true) + preferences.set(Keys.isExpertModeInfoDismissed, true) } override val isAutoStartBootEnabled: Flow = - preferences.get(Keys.isProModeAutoStartBootEnabled) - .map { it ?: PreferenceDefaults.PRO_MODE_AUTOSTART_BOOT } + preferences.get(Keys.isSystemBridgeKeepAliveEnabled) + .map { it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE } override fun toggleAutoStartBoot() { - preferences.update(Keys.isProModeAutoStartBootEnabled) { - !(it ?: PreferenceDefaults.PRO_MODE_AUTOSTART_BOOT) + preferences.update(Keys.isSystemBridgeKeepAliveEnabled) { + !(it ?: PreferenceDefaults.EXPERT_MODE_KEEP_ALIVE) } } @@ -238,11 +249,17 @@ class SystemBridgeSetupUseCaseImpl @Inject constructor( return when { accessibilityServiceState != AccessibilityServiceState.ENABLED -> SystemBridgeSetupStep.ACCESSIBILITY_SERVICE + !isNotificationPermissionGranted -> SystemBridgeSetupStep.NOTIFICATION_PERMISSION + !isDeveloperOptionsEnabled -> SystemBridgeSetupStep.DEVELOPER_OPTIONS + !isWifiConnected -> SystemBridgeSetupStep.WIFI_NETWORK + !isWirelessDebuggingEnabled -> SystemBridgeSetupStep.WIRELESS_DEBUGGING + isWirelessDebuggingEnabled -> SystemBridgeSetupStep.ADB_PAIRING + else -> SystemBridgeSetupStep.START_SERVICE } } @@ -262,6 +279,7 @@ interface SystemBridgeSetupUseCase { fun toggleSetupAssistant() val isSystemBridgeConnected: Flow + val isSystemBridgeStarting: Flow val nextSetupStep: Flow val isRootGranted: Flow @@ -281,7 +299,8 @@ interface SystemBridgeSetupUseCase { fun pairWirelessAdb() fun startSystemBridgeWithRoot() fun startSystemBridgeWithShizuku() - suspend fun startSystemBridgeWithAdb() + fun startSystemBridgeWithAdb() + fun autoStartSystemBridgeWithAdb() fun isCompatibleUsbModeSelected(): KMResult } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt index 4118c107f2..c5b71421a0 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/home/HomeKeyMapListScreen.kt @@ -135,11 +135,11 @@ fun HomeKeyMapListScreen( sheetState = sheetState, onDismissRequest = viewModel::dismissFixKeyEventActionBottomSheet, onEnableAccessibilityServiceClick = viewModel::onEnableAccessibilityServiceClick, - onEnableProModeClick = viewModel::onEnableProModeForKeyEventActionsClick, + onEnableExpertModeClick = viewModel::onEnableExpertModeForKeyEventActionsClick, onEnableInputMethodClick = viewModel::onEnableImeClick, onChooseInputMethodClick = viewModel::onChooseImeClick, onDoneClick = viewModel::dismissFixKeyEventActionBottomSheet, - onSelectProMode = viewModel::onSelectProMode, + onSelectExpertMode = viewModel::onSelectExpertMode, onSelectInputMethod = viewModel::onSelectInputMethod, onAutoSwitchImeCheckedChange = viewModel::onAutoSwitchImeCheckedChange, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt index 111593b800..4e85c46321 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/home/KeyMapListViewModel.kt @@ -336,9 +336,7 @@ class KeyMapListViewModel( Triple(listState, appBarState, showCreateKeyMapTapTarget) }.collectLatest { (listState, appBarState, showCreateKeyMapTapTarget) -> listState.ifIsData { list -> - if (list.isNotEmpty()) { - showFabText = false - } + showFabText = list.isEmpty() } _state.value = diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt new file mode 100644 index 0000000000..9dc7ea17d5 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevDevicesDelegate.kt @@ -0,0 +1,120 @@ +package io.github.sds100.keymapper.base.input + +import androidx.annotation.RequiresApi +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle +import io.github.sds100.keymapper.common.utils.Constants +import io.github.sds100.keymapper.common.utils.onFailure +import io.github.sds100.keymapper.common.utils.valueIfFailure +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.channels.Channel +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.receiveAsFlow +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import timber.log.Timber + +/** + * Need to use a cache that maps a device id to the other device information. This information + * could be sent in the onEvdevEvent callback instead, but sending non-primitive strings for the + * device name introduces extra overhead across Binder and JNI. + */ +@OptIn(ExperimentalCoroutinesApi::class) +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) +@Singleton +class EvdevDevicesDelegate @Inject constructor( + private val coroutineScope: CoroutineScope, + private val systemBridgeConnectionManager: SystemBridgeConnectionManager, +) { + private val grabbedDevicesById: MutableStateFlow> = + MutableStateFlow(emptyMap()) + + // Use a channel so there are no race conditions when grabbing and that all + // grab operations finish in the correct order to completion. + private val grabDevicesChannel: Channel> = Channel(capacity = 16) + + // All the evdev devices on the device, regardless of whether they are grabbed. + val allDevices: MutableStateFlow> = MutableStateFlow(emptyList()) + + init { + coroutineScope.launch { + systemBridgeConnectionManager.connectionState.collect { connectionState -> + when (connectionState) { + is SystemBridgeConnectionState.Connected -> { + allDevices.value = fetchAllDevices() + } + + is SystemBridgeConnectionState.Disconnected -> { + allDevices.value = emptyList() + grabbedDevicesById.value = emptyMap() + } + } + } + } + + coroutineScope.launch { + grabDevicesChannel.receiveAsFlow().collect { devices -> + withContext(Dispatchers.IO) { + invalidateGrabbedDevices(devices) + } + } + } + } + + private fun invalidateGrabbedDevices(devices: List) { + systemBridgeConnectionManager + .run { bridge -> bridge.setGrabTargets(devices.toTypedArray()) } + // The callback will respond with the new grabbed devices. + .onFailure { error -> + Timber.w( + "Grabbing devices failed in system bridge: $error", + ) + } + } + + fun setGrabTargets(devices: List) { + grabDevicesChannel.trySend(devices) + } + + fun getGrabbedDeviceInfo(id: Int): EvdevDeviceInfo? { + return grabbedDevicesById.value[id] + } + + fun getGrabbedDevices(): List { + return grabbedDevicesById.value.values.toList() + } + + fun onGrabbedDevicesChanged(devices: List) { + Timber.i("Grabbed devices changed: [${devices.joinToString { it.name }}]") + + grabbedDevicesById.value = + devices.associate { handle -> + handle.id to + EvdevDeviceInfo(handle.name, handle.bus, handle.vendor, handle.product) + } + } + + fun onEvdevDevicesChanged(devices: List) { + Timber.i("Evdev devices changed: [${devices.joinToString { it.name }}]") + + allDevices.value = devices + } + + private suspend fun fetchAllDevices(): List { + // Do it on a separate thread in case there is deadlock + return withContext(Dispatchers.IO) { + systemBridgeConnectionManager.run { bridge -> + bridge.evdevInputDevices?.filterNotNull() ?: emptyList() + } + }.onFailure { error -> + Timber.e("Failed to get evdev input devices from system bridge: $error") + }.valueIfFailure { emptyList() } + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt deleted file mode 100644 index aa592e491e..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/EvdevHandleCache.kt +++ /dev/null @@ -1,97 +0,0 @@ -package io.github.sds100.keymapper.base.input - -import androidx.annotation.RequiresApi -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle -import io.github.sds100.keymapper.common.models.EvdevDeviceInfo -import io.github.sds100.keymapper.common.utils.Constants -import io.github.sds100.keymapper.common.utils.onFailure -import io.github.sds100.keymapper.common.utils.valueIfFailure -import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager -import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState -import io.github.sds100.keymapper.sysbridge.manager.isConnected -import io.github.sds100.keymapper.system.devices.DevicesAdapter -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collect -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext -import timber.log.Timber - -@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) -@Singleton -class EvdevHandleCache @Inject constructor( - private val coroutineScope: CoroutineScope, - private val devicesAdapter: DevicesAdapter, - private val systemBridgeConnectionManager: SystemBridgeConnectionManager, -) { - private val devicesByPath: MutableStateFlow> = - MutableStateFlow(emptyMap()) - - val devices: StateFlow> = - devicesByPath - .map { pathMap -> - pathMap.values.map { device -> - EvdevDeviceInfo( - name = device.name, - bus = device.bus, - vendor = device.vendor, - product = device.product, - ) - } - } - .stateIn(coroutineScope, SharingStarted.Eagerly, emptyList()) - - init { - coroutineScope.launch { - combine( - devicesAdapter.connectedInputDevices, - systemBridgeConnectionManager.connectionState, - ) { _, connectionState -> - if (connectionState is SystemBridgeConnectionState.Connected) { - invalidate() - } else { - devicesByPath.value = emptyMap() - } - }.collect() - } - } - - fun getByPath(path: String): EvdevDeviceHandle? { - return devicesByPath.value[path] - } - - fun getByInfo(deviceInfo: EvdevDeviceInfo): EvdevDeviceHandle? { - return devicesByPath.value.values.firstOrNull { - it.name == deviceInfo.name && - it.bus == deviceInfo.bus && - it.vendor == deviceInfo.vendor && - it.product == deviceInfo.product - } - } - - suspend fun invalidate() { - if (!systemBridgeConnectionManager.isConnected()) { - devicesByPath.value = emptyMap() - return - } - - // Do it on a separate thread in case there is deadlock - val newDevices = withContext(Dispatchers.IO) { - systemBridgeConnectionManager.run { bridge -> - bridge.evdevInputDevices.associateBy { it.path } - } - }.onFailure { error -> - Timber.e("Failed to get evdev input devices from system bridge $error") - }.valueIfFailure { emptyMap() } - - devicesByPath.value = newDevices - } -} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt index e8d73beb13..3feaad3905 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/input/InputEventHub.kt @@ -6,18 +6,18 @@ import androidx.annotation.RequiresApi import io.github.sds100.keymapper.base.BuildConfig import io.github.sds100.keymapper.base.system.inputmethod.ImeInputEventInjector import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.utils.Constants 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.firstBlocking -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.common.utils.valueOrNull import io.github.sds100.keymapper.data.Keys import io.github.sds100.keymapper.data.repositories.PreferenceRepository -import io.github.sds100.keymapper.sysbridge.IEvdevCallback +import io.github.sds100.keymapper.evdev.IEvdevCallback import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState import io.github.sds100.keymapper.sysbridge.manager.isConnected @@ -34,11 +34,8 @@ import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.channels.Channel import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.collectLatest -import kotlinx.coroutines.flow.consumeAsFlow import kotlinx.coroutines.flow.filter import kotlinx.coroutines.flow.filterIsInstance -import kotlinx.coroutines.flow.flowOn import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.launch @@ -51,7 +48,7 @@ class InputEventHubImpl @Inject constructor( private val systemBridgeConnManager: SystemBridgeConnectionManager, private val imeInputEventInjector: ImeInputEventInjector, private val preferenceRepository: PreferenceRepository, - private val evdevHandlesCache: EvdevHandleCache, + private val evdevDevicesDelegate: EvdevDevicesDelegate, ) : IEvdevCallback.Stub(), InputEventHub { @@ -73,8 +70,6 @@ class InputEventHubImpl @Inject constructor( } }.stateIn(coroutineScope, SharingStarted.Eagerly, false) - private val invalidateGrabbedDevicesChannel: Channel = Channel(capacity = 10) - init { startKeyEventProcessingLoop() @@ -83,23 +78,14 @@ class InputEventHubImpl @Inject constructor( systemBridgeConnManager.connectionState .filterIsInstance() .collect { + // Whenever the system bridge is connected systemBridgeConnManager.run { bridge -> bridge.registerEvdevCallback(this@InputEventHubImpl) + }.onSuccess { + invalidateGrabbedDevices() } } } - - coroutineScope.launch { - invalidateGrabbedDevicesChannel - .consumeAsFlow() - .map { - clients.values.flatMap { it.grabbedEvdevDevices }.toSet().toList() - } - .flowOn(Dispatchers.Default) - .collectLatest { devices -> - invalidateGrabbedEvdevDevices(devices) - } - } } } @@ -123,17 +109,9 @@ class InputEventHubImpl @Inject constructor( return systemBridgeConnManager.isConnected() } - override fun onEvdevEventLoopStarted() { - Timber.i("On evdev event loop started") - - coroutineScope.launch { - invalidateGrabbedDevicesChannel.send(Unit) - } - } - @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) override fun onEvdevEvent( - devicePath: String?, + deviceId: Int, timeSec: Long, timeUsec: Long, type: Int, @@ -141,10 +119,9 @@ class InputEventHubImpl @Inject constructor( value: Int, androidCode: Int, ): Boolean { - devicePath ?: return false - - val handle = evdevHandlesCache.getByPath(devicePath) ?: return false - val evdevEvent = KMEvdevEvent(handle, type, code, value, androidCode, timeSec, timeUsec) + val info = evdevDevicesDelegate.getGrabbedDeviceInfo(deviceId) ?: return false + val evdevEvent = + KMEvdevEvent(deviceId, info, type, code, value, androidCode, timeSec, timeUsec) return onInputEvent(evdevEvent, InputEventDetectionSource.EVDEV) } @@ -162,20 +139,13 @@ class InputEventHubImpl @Inject constructor( for (clientContext in clients.values) { if (event is KMEvdevEvent) { if (!clientContext.evdevEventTypes.contains(event.type) || - clientContext.grabbedEvdevDevices.isEmpty() + clientContext.grabRequests.isEmpty() ) { continue } - val deviceInfo = EvdevDeviceInfo( - event.device.name, - event.device.bus, - event.device.vendor, - event.device.product, - ) - // Only send events from evdev devices to the client if they grabbed it - if (!clientContext.grabbedEvdevDevices.contains(deviceInfo)) { + if (!clientContext.grabbedDevice(event.deviceInfo)) { continue } @@ -200,7 +170,7 @@ class InputEventHubImpl @Inject constructor( when (event) { is KMEvdevEvent -> { Timber.d( - "Evdev event: devicePath=${event.device.path}, deviceName=${event.device.name}, type=${event.type}, code=${event.code}, value=${event.value}", + "Evdev event: deviceId=${event.deviceId}, deviceName=${event.deviceInfo.name}, type=${event.type}, code=${event.code}, value=${event.value}", ) } @@ -214,17 +184,21 @@ class InputEventHubImpl @Inject constructor( when (event.action) { KeyEvent.ACTION_DOWN -> { Timber.d( - "Key down ${KeyEvent.keyCodeToString( - event.keyCode, - )}: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", + "Key down ${ + KeyEvent.keyCodeToString( + event.keyCode, + ) + }: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", ) } KeyEvent.ACTION_UP -> { Timber.d( - "Key up ${KeyEvent.keyCodeToString( - event.keyCode, - )}: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", + "Key up ${ + KeyEvent.keyCodeToString( + event.keyCode, + ) + }: keyCode=${event.keyCode}, scanCode=${event.scanCode}, deviceId=${event.deviceId}, metaState=${event.metaState}, source=${event.source}", ) } @@ -247,24 +221,34 @@ class InputEventHubImpl @Inject constructor( } clients[clientId] = ClientContext(callback, emptySet(), evdevEventTypes.toSet()) + if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { + invalidateGrabbedDevices() + } } override fun unregisterClient(clientId: String) { Timber.d("InputEventHub: Unregistering client $clientId") clients.remove(clientId) - invalidateGrabbedDevicesChannel.trySend(Unit) + if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { + invalidateGrabbedDevices() + } } - override fun setGrabbedEvdevDevices(clientId: String, devices: List) { + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) + override fun setGrabTargets(clientId: String, devices: List) { if (!clients.containsKey(clientId)) { throw IllegalArgumentException( "This client $clientId is not registered when trying to grab devices!", ) } - clients[clientId] = clients[clientId]!!.copy(grabbedEvdevDevices = devices.toSet()) + clients[clientId] = clients[clientId]!!.copy(grabRequests = devices.toSet()) + invalidateGrabbedDevices() + } - invalidateGrabbedDevicesChannel.trySend(Unit) + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) + override fun getGrabbedDevices(): List { + return evdevDevicesDelegate.getGrabbedDevices() } @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) @@ -275,60 +259,64 @@ class InputEventHubImpl @Inject constructor( ) } - val devices = evdevHandlesCache.devices.value.toSet() - clients[clientId] = clients[clientId]!!.copy(grabbedEvdevDevices = devices) + val devices = evdevDevicesDelegate.allDevices.value + val grabRequests = devices.map { + GrabTargetKeyCode( + name = it.name, + bus = it.bus, + vendor = it.vendor, + product = it.product, + extraKeyCodes = intArrayOf(), + ) + }.toSet() + clients[clientId] = clients[clientId]!!.copy(grabRequests = grabRequests) - invalidateGrabbedDevicesChannel.trySend(Unit) - } - - @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) - private suspend fun invalidateGrabbedEvdevDevices(evdevDevices: List) { - // Invalidate the cache first to make sure it is up to date. - evdevHandlesCache.invalidate() - - // Grabbing can block if there are other grabbing or event loop start/stop operations happening. - systemBridgeConnManager.run { bridge -> bridge.ungrabAllEvdevDevices() } - .onSuccess { Timber.i("Ungrabbed all evdev devices: $it") } - .then { - val handles: Array = - evdevDevices.mapNotNull { evdevHandlesCache.getByInfo(it)?.path }.toTypedArray() - - val result = - systemBridgeConnManager.run { bridge -> bridge.grabEvdevDeviceArray(handles) } - - if (result.valueOrNull() == true) { - Success(Unit) - } else { - KMError.Exception(Exception("Failed to grab")) - } - } - .onSuccess { result -> - Timber.i("Grabbed evdev devices [${evdevDevices.joinToString { it.name }}]") - } - .onFailure { error -> - // Do not log if it is expected to prevent log spam. - if (error !is SystemBridgeError.Disconnected) { - Timber.e("Failed to grab evdev devices.") - } - } + invalidateGrabbedDevices() } @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) override fun injectEvdevEvent( - devicePath: String, + deviceId: Int, type: Int, code: Int, value: Int, - ): KMResult { + ): KMResult { return systemBridgeConnManager.run { bridge -> bridge.writeEvdevEvent( - devicePath, + deviceId, type, code, value, ) - }.onSuccess { - Timber.d("Injected evdev event: $it") + }.then { upstreamSuccess -> + if (upstreamSuccess) { + Success(Unit) + } else { + Timber.e( + "Failed to write evdev event: deviceId=$deviceId, type=$type, code=$code, value=$value", + ) + SystemBridgeError.WriteEvdevEventFailed + } + } + } + + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) + override fun injectEvdevEventKeyCode(deviceId: Int, keyCode: Int, value: Int): KMResult { + return systemBridgeConnManager.run { bridge -> + bridge.writeEvdevEventKeyCode( + deviceId, + keyCode, + value, + ) + }.then { upstreamSuccess -> + if (upstreamSuccess) { + Success(Unit) + } else { + Timber.e( + "Failed to write evdev event with key code: deviceId=$deviceId, keycode=$keyCode, value=$value", + ) + SystemBridgeError.WriteEvdevEventFailed + } } } @@ -383,14 +371,40 @@ class InputEventHubImpl @Inject constructor( .firstBlocking() } + @RequiresApi(Build.VERSION_CODES.Q) + override fun onGrabbedDevicesChanged(devices: Array?) { + val devicesList = devices?.filterNotNull()?.toList() ?: emptyList() + evdevDevicesDelegate.onGrabbedDevicesChanged(devicesList) + } + + @RequiresApi(Build.VERSION_CODES.Q) + override fun onEvdevDevicesChanged(devices: Array?) { + val devicesList = devices?.filterNotNull()?.toList() ?: emptyList() + evdevDevicesDelegate.onEvdevDevicesChanged(devicesList) + } + + @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) + private fun invalidateGrabbedDevices() { + val devicesToGrab = clients.values.flatMap { it.grabRequests }.toSet() + evdevDevicesDelegate.setGrabTargets(devicesToGrab.toList()) + } + private data class ClientContext( val callback: InputEventHubCallback, /** * The evdev devices that this client wants to grab. */ - val grabbedEvdevDevices: Set, + val grabRequests: Set, val evdevEventTypes: Set, - ) + ) { + private val devicesSet: Set = grabRequests.map { + EvdevDeviceInfo(name = it.name, bus = it.bus, vendor = it.vendor, product = it.product) + }.toSet() + + fun grabbedDevice(device: EvdevDeviceInfo): Boolean { + return devicesSet.contains(device) + } + } } interface InputEventHub { @@ -408,7 +422,8 @@ interface InputEventHub { fun unregisterClient(clientId: String) - fun setGrabbedEvdevDevices(clientId: String, devices: List) + fun getGrabbedDevices(): List + fun setGrabTargets(clientId: String, devices: List) fun grabAllEvdevDevices(clientId: String) /** @@ -429,7 +444,13 @@ interface InputEventHub { */ fun injectKeyEventAsync(event: InjectKeyEventModel): KMResult - fun injectEvdevEvent(devicePath: String, type: Int, code: Int, value: Int): KMResult + fun injectEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int): KMResult + + /** + * This injects an Android key code and the system bridge will handle the mapping to + * the linux scan code. + */ + fun injectEvdevEventKeyCode(deviceId: Int, keyCode: Int, value: Int): KMResult /** * Send an input event to the connected clients. diff --git a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt index 74f7a31df3..b21b126d86 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/keymaps/DisplayKeyMapUseCase.kt @@ -7,7 +7,7 @@ import io.github.sds100.keymapper.base.actions.DisplayActionUseCase import io.github.sds100.keymapper.base.actions.GetActionErrorUseCase import io.github.sds100.keymapper.base.constraints.DisplayConstraintUseCase import io.github.sds100.keymapper.base.constraints.GetConstraintErrorUseCase -import io.github.sds100.keymapper.base.input.EvdevHandleCache +import io.github.sds100.keymapper.base.input.EvdevDevicesDelegate import io.github.sds100.keymapper.base.purchasing.ProductId import io.github.sds100.keymapper.base.purchasing.PurchasingError.ProductNotPurchased import io.github.sds100.keymapper.base.purchasing.PurchasingManager @@ -70,7 +70,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( private val buildConfigProvider: BuildConfigProvider, private val navigationProvider: NavigationProvider, private val systemBridgeConnectionManager: SystemBridgeConnectionManager, - private val evdevHandleCache: EvdevHandleCache, + private val grabbedEvdevDeviceCache: EvdevDevicesDelegate, ) : DisplayKeyMapUseCase, GetActionErrorUseCase by getActionErrorUseCase, GetConstraintErrorUseCase by getConstraintErrorUseCase { @@ -113,7 +113,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( private val evdevDevices: Flow?> = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - evdevHandleCache.devices + grabbedEvdevDeviceCache.allDevices } else { flowOf(null) } @@ -163,6 +163,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( TriggerError.CANT_DETECT_IN_PHONE_CALL -> fixError( KMError.CantDetectKeyEventsInPhoneCall, ) + TriggerError.ASSISTANT_TRIGGER_NOT_PURCHASED -> fixError( ProductNotPurchased( ProductId.ASSISTANT_TRIGGER, @@ -170,6 +171,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( ) TriggerError.DPAD_IME_NOT_SELECTED -> fixError(KMError.DpadTriggerImeNotSelected) + TriggerError.FLOATING_BUTTONS_NOT_PURCHASED -> fixError( ProductNotPurchased( ProductId.FLOATING_BUTTONS, @@ -177,7 +179,9 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( ) TriggerError.PURCHASE_VERIFICATION_FAILED -> purchasingManager.refresh() + TriggerError.SYSTEM_BRIDGE_DISCONNECTED -> fixError(SystemBridgeError.Disconnected) + TriggerError.EVDEV_DEVICE_NOT_FOUND, TriggerError.FLOATING_BUTTON_DELETED, TriggerError.SYSTEM_BRIDGE_UNSUPPORTED, @@ -200,18 +204,24 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( override suspend fun fixError(error: KMError) { when (error) { is KMError.AppDisabled -> packageManagerAdapter.enableApp(error.packageName) + is KMError.AppNotFound -> packageManagerAdapter.downloadApp(error.packageName) + KMError.NoCompatibleImeChosen -> keyMapperImeHelper.chooseCompatibleInputMethod().otherwise { inputMethodAdapter.showImePicker(fromForeground = true) } KMError.NoCompatibleImeEnabled -> keyMapperImeHelper.enableCompatibleInputMethods() + is ImeDisabled -> switchImeInterface.enableIme(error.ime.id) + is PermissionDenied -> permissionAdapter.request(error.permission) + is KMError.ShizukuNotStarted -> packageManagerAdapter.openApp( ShizukuUtils.SHIZUKU_PACKAGE, ) + is KMError.CantDetectKeyEventsInPhoneCall -> { if (!keyMapperImeHelper.isCompatibleImeEnabled()) { keyMapperImeHelper.enableCompatibleInputMethods() @@ -227,7 +237,7 @@ class DisplayKeyMapUseCaseImpl @Inject constructor( is SystemBridgeError.Disconnected -> navigationProvider.navigate( "fix_system_bridge", - NavDestination.ProMode, + NavDestination.ExpertMode, ) is KMError.DpadTriggerImeNotSelected -> { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt b/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt new file mode 100644 index 0000000000..1deffaf591 --- /dev/null +++ b/base/src/main/java/io/github/sds100/keymapper/base/logging/SystemBridgeLogger.kt @@ -0,0 +1,85 @@ +package io.github.sds100.keymapper.base.logging + +import android.util.Log +import androidx.annotation.RequiresApi +import io.github.sds100.keymapper.common.utils.Constants +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.data.repositories.PreferenceRepository +import io.github.sds100.keymapper.sysbridge.ILogCallback +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import javax.inject.Inject +import javax.inject.Singleton +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.map +import kotlinx.coroutines.flow.stateIn +import kotlinx.coroutines.launch +import timber.log.Timber + +/** + * Listens for SystemBridge connection and registers a log callback to receive + * log messages from the Rust SystemBridge code. Respects the "extra logging" + * preference to control the log level. + */ +@Singleton +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) +class SystemBridgeLogger @Inject constructor( + private val coroutineScope: CoroutineScope, + private val systemBridgeConnManager: SystemBridgeConnectionManager, + private val preferenceRepository: PreferenceRepository, +) : ILogCallback.Stub() { + + private val extraLoggingEnabled: StateFlow = + preferenceRepository.get(Keys.log) + .map { it ?: false } + .stateIn(coroutineScope, SharingStarted.Eagerly, false) + + fun start() { + // Listen for connection state changes + coroutineScope.launch { + systemBridgeConnManager.connectionState + .filterIsInstance() + .collect { + registerWithSystemBridge() + } + } + + // Listen for preference changes and update log level + coroutineScope.launch { + extraLoggingEnabled.collect { enabled -> + updateLogLevel(enabled) + } + } + } + + private fun registerWithSystemBridge() { + systemBridgeConnManager.run { bridge -> + bridge.registerLogCallback(this@SystemBridgeLogger) + bridge.setLogLevel(getSystemBridgeLogLevel(extraLoggingEnabled.value)) + } + } + + private fun updateLogLevel(extraLogging: Boolean) { + systemBridgeConnManager.run { bridge -> + bridge.setLogLevel(getSystemBridgeLogLevel(extraLogging)) + } + } + + override fun onLog(level: Int, message: String) { + // Log with Timber so the messages appear under the Key Mapper package name + // in logcat, and the KeyMapperLoggingTree will then save it to the LogRepository. + Timber.log(priority = level, message = "systembridge: $message") + } + + private fun getSystemBridgeLogLevel(extraLogging: Boolean): Int { + val level = if (extraLogging) { + Log.DEBUG + } else { + Log.INFO + } + return level + } +} diff --git a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt index 33ced838b9..da7f2078fd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt @@ -49,8 +49,9 @@ class OnboardingTipDelegateImpl @Inject constructor( private const val PARALLEL_TRIGGER_TIP_ID = "parallel_trigger_tip" private const val SEQUENCE_TRIGGER_TIP_ID = "sequence_trigger_tip" private const val TRIGGER_CONSTRAINTS_TIP_ID = "trigger_constraints_tip" - const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = "caps_lock_pro_mode_compatibility_tip" - const val VOLUME_BUTTONS_PRO_MODE_TIP_ID = "volume_buttons_pro_mode_tip" + const val CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID = + "caps_lock_expert_mode_compatibility_tip" + const val VOLUME_BUTTONS_PRO_MODE_TIP_ID = "volume_buttons_expert_mode_tip" const val SCREEN_PINNING_TIP_ID = "screen_pinning_tip" const val IME_DETECTION_TIP_ID = "ime_detection_tip" const val RINGER_MODE_TIP_ID = "ringer_mode_tip" @@ -74,13 +75,13 @@ class OnboardingTipDelegateImpl @Inject constructor( false, ) - private var shownCapsLockProModeTip: Boolean by PrefDelegate( - Keys.shownCapsLockProModeTip, + private var shownCapsLockExpertModeTip: Boolean by PrefDelegate( + Keys.shownCapsLockExpertModeTip, false, ) - private var shownVolumeButtonsProModeTip: Boolean by PrefDelegate( - Keys.shownVolumeButtonsProModeTip, + private var shownVolumeButtonsExpertModeTip: Boolean by PrefDelegate( + Keys.shownVolumeButtonsExpertModeTip, false, ) @@ -141,13 +142,13 @@ class OnboardingTipDelegateImpl @Inject constructor( when (tipId) { RINGER_MODE_TIP_ID -> { viewModelScope.launch { - navigate("ringer_mode_tip_pro_mode", NavDestination.ProMode) + navigate("ringer_mode_tip_expert_mode", NavDestination.ExpertMode) } } VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { viewModelScope.launch { - navigate("volume_buttons_pro_mode_tip", NavDestination.ProMode) + navigate("volume_buttons_expert_mode_tip", NavDestination.ExpertMode) } } } @@ -188,8 +189,8 @@ class OnboardingTipDelegateImpl @Inject constructor( showPowerButtonEmergencyTip -> { val tipModel = OnboardingTipModel( id = POWER_BUTTON_EMERGENCY_TIP_ID, - title = getString(R.string.pro_mode_emergency_tip_title), - message = getString(R.string.pro_mode_emergency_tip_text), + title = getString(R.string.expert_mode_emergency_tip_title), + message = getString(R.string.expert_mode_emergency_tip_text), isDismissable = false, ) @@ -218,24 +219,24 @@ class OnboardingTipDelegateImpl @Inject constructor( triggerTip.value = tipModel } - hasVolumeKey && !shownVolumeButtonsProModeTip -> { + hasVolumeKey && !shownVolumeButtonsExpertModeTip -> { val tip = OnboardingTipModel( id = VOLUME_BUTTONS_PRO_MODE_TIP_ID, - title = getString(R.string.tip_volume_buttons_pro_mode_title), - message = getString(R.string.tip_volume_buttons_pro_mode_text), + title = getString(R.string.tip_volume_buttons_expert_mode_title), + message = getString(R.string.tip_volume_buttons_expert_mode_text), isDismissable = true, - buttonText = getString(R.string.tip_volume_buttons_pro_mode_button), + buttonText = getString(R.string.tip_volume_buttons_expert_mode_button), ) triggerTip.value = tip } - hasCapsLockKey && !shownCapsLockProModeTip -> { + hasCapsLockKey && !shownCapsLockExpertModeTip -> { val tip = OnboardingTipModel( id = CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID, - title = getString(R.string.tip_caps_lock_pro_mode_title), - message = getString(R.string.tip_caps_lock_pro_mode_text), + title = getString(R.string.tip_caps_lock_expert_mode_title), + message = getString(R.string.tip_caps_lock_expert_mode_text), isDismissable = true, - buttonText = getString(R.string.tip_caps_lock_pro_mode_button), + buttonText = getString(R.string.tip_caps_lock_expert_mode_button), ) triggerTip.value = tip } @@ -293,11 +294,11 @@ class OnboardingTipDelegateImpl @Inject constructor( } CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID -> { - shownCapsLockProModeTip = true + shownCapsLockExpertModeTip = true } VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { - shownVolumeButtonsProModeTip = true + shownVolumeButtonsExpertModeTip = true } SCREEN_PINNING_TIP_ID -> { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt index 53cb6b84ea..272d514f0f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/onboarding/SetupAccessibilityServiceDialog.kt @@ -167,7 +167,7 @@ private fun CantFindAccessibilitySettingsDialog( }, confirmButton = { TextButton(onClick = onOpenGuide) { - Text(stringResource(R.string.pos_start_service_with_adb_guide)) + Text(stringResource(R.string.pos_start_service_with_expert_mode)) } }, dismissButton = { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt deleted file mode 100644 index d246ed0b8f..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/promode/ProModeSetupViewModel.kt +++ /dev/null @@ -1,94 +0,0 @@ -package io.github.sds100.keymapper.base.promode - -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.ui.ResourceProvider -import io.github.sds100.keymapper.common.utils.State -import io.github.sds100.keymapper.common.utils.dataOrNull -import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupStep -import javax.inject.Inject -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch - -@HiltViewModel -class ProModeSetupViewModel @Inject constructor( - private val useCase: SystemBridgeSetupUseCase, - navigationProvider: NavigationProvider, - resourceProvider: ResourceProvider, -) : ViewModel(), - NavigationProvider by navigationProvider, - ResourceProvider by resourceProvider { - val setupState: StateFlow> = - combine(useCase.nextSetupStep, useCase.isSetupAssistantEnabled, ::buildState).stateIn( - viewModelScope, - SharingStarted.Eagerly, - State.Loading, - ) - - fun onStepButtonClick() { - // Do not check the latest value in the use case because there is significant latency - // when it is checking whether it is paired - val currentStep = setupState.value.dataOrNull()?.step ?: return - - when (currentStep) { - SystemBridgeSetupStep.ACCESSIBILITY_SERVICE -> useCase.enableAccessibilityService() - SystemBridgeSetupStep.NOTIFICATION_PERMISSION -> useCase.requestNotificationPermission() - SystemBridgeSetupStep.DEVELOPER_OPTIONS -> useCase.enableDeveloperOptions() - SystemBridgeSetupStep.WIFI_NETWORK -> useCase.connectWifiNetwork() - SystemBridgeSetupStep.WIRELESS_DEBUGGING -> useCase.enableWirelessDebugging() - SystemBridgeSetupStep.ADB_PAIRING -> useCase.pairWirelessAdb() - SystemBridgeSetupStep.START_SERVICE -> viewModelScope.launch { - useCase.startSystemBridgeWithAdb() - } - SystemBridgeSetupStep.STARTED -> viewModelScope.launch { popBackStack() } - } - } - - fun onBackClick() { - viewModelScope.launch { - popBackStack() - } - } - - fun onAssistantClick() { - useCase.toggleSetupAssistant() - } - - private fun buildState( - step: SystemBridgeSetupStep, - isSetupAssistantUserEnabled: Boolean, - ): State.Data { - // Uncheck the setup assistant if the accessibility service is disabled since it is - // required for the setup assistant to work - val isSetupAssistantChecked = if (step == SystemBridgeSetupStep.ACCESSIBILITY_SERVICE) { - false - } else { - isSetupAssistantUserEnabled - } - - return State.Data( - ProModeSetupState( - stepNumber = step.stepIndex + 1, - stepCount = SystemBridgeSetupStep.entries.size, - step = step, - isSetupAssistantChecked = isSetupAssistantChecked, - isSetupAssistantButtonEnabled = - step != SystemBridgeSetupStep.ACCESSIBILITY_SERVICE && - step != SystemBridgeSetupStep.STARTED, - ), - ) - } -} - -data class ProModeSetupState( - val stepNumber: Int, - val stepCount: Int, - val step: SystemBridgeSetupStep, - val isSetupAssistantChecked: Boolean, - val isSetupAssistantButtonEnabled: Boolean, -) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/ConfigSettingsUseCase.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/ConfigSettingsUseCase.kt index baf703f049..183761e9ba 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/ConfigSettingsUseCase.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/ConfigSettingsUseCase.kt @@ -57,7 +57,7 @@ class ConfigSettingsUseCaseImpl @Inject constructor( Theme.entries.single { it.value == value.toInt() } } - override val isRootGranted: Flow = suAdapter.isRootGranted + override val isRootGranted: Flow = suAdapter.isRootGranted.map { it ?: false } override val isWriteSecureSettingsGranted: Flow = channelFlow { send(permissionAdapter.isGranted(Permission.WRITE_SECURE_SETTINGS)) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt index e9283a37e2..9cbcb0ed7a 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsScreen.kt @@ -24,6 +24,7 @@ import androidx.compose.material.icons.outlined.Android import androidx.compose.material.icons.outlined.BugReport import androidx.compose.material.icons.outlined.FindInPage import androidx.compose.material.icons.outlined.Gamepad +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.Code import androidx.compose.material.icons.rounded.Construction import androidx.compose.material.icons.rounded.Devices @@ -69,14 +70,13 @@ import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText import io.github.sds100.keymapper.base.utils.ui.compose.SwitchPreferenceCompose import io.github.sds100.keymapper.base.utils.ui.compose.icons.FolderManaged import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon import io.github.sds100.keymapper.base.utils.ui.compose.icons.WandStars import io.github.sds100.keymapper.common.utils.BuildUtils import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.system.files.FileUtils import kotlinx.coroutines.launch -private val isProModeSupported = Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API +private val isExpertModeSupported = Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API private val isAutoSwitchImeSupported = Build.VERSION.SDK_INT >= Build.VERSION_CODES.R @Composable @@ -149,9 +149,9 @@ fun SettingsScreen(modifier: Modifier = Modifier, viewModel: SettingsViewModel) onThemeSelected = viewModel::onThemeSelected, onPauseResumeNotificationClick = viewModel::onPauseResumeNotificationClick, onDefaultOptionsClick = viewModel::onDefaultOptionsClick, - onProModeClick = { - if (isProModeSupported) { - viewModel.onProModeClick() + onExpertModeClick = { + if (isExpertModeSupported) { + viewModel.onExpertModeClick() } else { scope.launch { snackbarHostState.showSnackbar( @@ -245,7 +245,7 @@ private fun Content( onPauseResumeNotificationClick: () -> Unit = { }, onDefaultOptionsClick: () -> Unit = { }, onAutomaticBackupClick: () -> Unit = { }, - onProModeClick: () -> Unit = { }, + onExpertModeClick: () -> Unit = { }, onAutomaticChangeImeClick: () -> Unit = { }, onForceVibrateToggled: (Boolean) -> Unit = { }, onLoggingToggled: (Boolean) -> Unit = { }, @@ -253,7 +253,7 @@ private fun Content( onShareLogcatClick: () -> Unit = { }, onHideHomeScreenAlertsToggled: (Boolean) -> Unit = { }, onShowDeviceDescriptorsToggled: (Boolean) -> Unit = { }, - onKeyEventActionMethodSelected: (isProModeSelected: Boolean) -> Unit = {}, + onKeyEventActionMethodSelected: (isExpertModeSelected: Boolean) -> Unit = {}, ) { Column( modifier @@ -359,23 +359,23 @@ private fun Content( modifier = Modifier .fillMaxWidth() .padding(horizontal = 16.dp), - isProModeSelected = state.keyEventActionsUseSystemBridege, + isExpertModeSelected = state.keyEventActionsUseSystemBridge, onSelected = onKeyEventActionMethodSelected, ) OptionPageButton( - title = stringResource(R.string.title_pref_pro_mode), - text = if (isProModeSupported) { - stringResource(R.string.summary_pref_pro_mode) + title = stringResource(R.string.title_pref_expert_mode), + text = if (isExpertModeSupported) { + stringResource(R.string.summary_pref_expert_mode) } else { stringResource( R.string.error_sdk_version_too_low, BuildUtils.getSdkVersionName(Constants.SYSTEM_BRIDGE_MIN_API), ) }, - icon = KeyMapperIcons.ProModeIcon, - onClick = onProModeClick, - enabled = isProModeSupported, + icon = Icons.Outlined.OfflineBolt, + onClick = onExpertModeClick, + enabled = isExpertModeSupported, ) OptionPageButton( @@ -428,13 +428,13 @@ private fun Content( @Composable private fun KeyEventActionMethodRow( modifier: Modifier = Modifier, - isProModeSelected: Boolean, - onSelected: (isProModeSelected: Boolean) -> Unit, + isExpertModeSelected: Boolean, + onSelected: (isExpertModeSelected: Boolean) -> Unit, ) { Column(modifier) { val buttonStates = listOf( false to stringResource(R.string.fix_key_event_action_input_method_title), - true to stringResource(R.string.pro_mode_app_bar_title), + true to stringResource(R.string.expert_mode_app_bar_title), ) Text( @@ -454,11 +454,11 @@ private fun KeyEventActionMethodRow( horizontalArrangement = Arrangement.spacedBy(16.dp), verticalArrangement = Arrangement.spacedBy(4.dp), ) { - for ((isProMode, text) in buttonStates) { + for ((isExpertMode, text) in buttonStates) { RadioButtonText( text = text, - isSelected = isProMode == isProModeSelected, - onSelected = { onSelected(isProMode) }, + isSelected = isExpertMode == isExpertModeSelected, + onSelected = { onSelected(isExpertMode) }, ) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt index 540bf753fa..7628eb1edc 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/settings/SettingsViewModel.kt @@ -67,7 +67,7 @@ class SettingsViewModel @Inject constructor( forceVibrate = values[3] as Boolean? ?: false, hideHomeScreenAlerts = values[4] as Boolean? ?: false, showDeviceDescriptors = values[5] as Boolean? ?: false, - keyEventActionsUseSystemBridege = values[6] as Boolean? ?: false, + keyEventActionsUseSystemBridge = values[6] as Boolean? ?: false, ) }.stateIn(viewModelScope, SharingStarted.Lazily, MainSettingsState()) @@ -171,9 +171,9 @@ class SettingsViewModel @Inject constructor( useCase.requestRootPermission() } - fun onProModeClick() { + fun onExpertModeClick() { viewModelScope.launch { - navigate("pro_mode_settings", NavDestination.ProMode) + navigate("expert_mode_settings", NavDestination.ExpertMode) } } @@ -323,9 +323,9 @@ class SettingsViewModel @Inject constructor( } } - fun onKeyEventActionMethodSelected(isProModeSelected: Boolean) { + fun onKeyEventActionMethodSelected(isExpertModeSelected: Boolean) { viewModelScope.launch { - useCase.setPreference(Keys.keyEventActionsUseSystemBridge, isProModeSelected) + useCase.setPreference(Keys.keyEventActionsUseSystemBridge, isExpertModeSelected) } } @@ -360,7 +360,7 @@ data class MainSettingsState( val loggingEnabled: Boolean = false, val hideHomeScreenAlerts: Boolean = false, val showDeviceDescriptors: Boolean = false, - val keyEventActionsUseSystemBridege: Boolean = false, + val keyEventActionsUseSystemBridge: Boolean = false, ) data class DefaultSettingsState( diff --git a/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt b/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt index bb53bd5f66..a48843773b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/sorting/comparators/KeyMapConstraintsComparator.kt @@ -140,6 +140,9 @@ class KeyMapConstraintsComparator( ConstraintData.HingeOpen -> Success("") ConstraintData.KeyboardNotShowing -> Success("") ConstraintData.KeyboardShowing -> Success("") + is ConstraintData.PhysicalOrientation -> Success( + constraint.data.physicalOrientation.toString(), + ) } } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt index 0e286e3296..ad8ea69ca2 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityService.kt @@ -227,8 +227,12 @@ abstract class BaseAccessibilityService : fingerprintGestureController.registerFingerprintGestureCallback(it, null) } - gestureHandlerThread.start() - gestureHandler = Handler(gestureHandlerThread.looper) + try { + gestureHandlerThread.start() + gestureHandler = Handler(gestureHandlerThread.looper) + } catch (_: IllegalThreadStateException) { + // do nothing if onServiceConnected is called again + } } override fun onUnbind(intent: Intent?): Boolean { diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt index 065224ad53..5f36cb46f9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/accessibility/BaseAccessibilityServiceController.kt @@ -17,12 +17,12 @@ import io.github.sds100.keymapper.base.constraints.DetectConstraintsUseCaseImpl import io.github.sds100.keymapper.base.detection.DetectKeyMapsUseCaseImpl import io.github.sds100.keymapper.base.detection.KeyMapDetectionController import io.github.sds100.keymapper.base.detection.TriggerKeyMapFromOtherAppsController +import io.github.sds100.keymapper.base.expertmode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.input.InputEventDetectionSource import io.github.sds100.keymapper.base.input.InputEventHub import io.github.sds100.keymapper.base.keymaps.FingerprintGesturesSupportedUseCase import io.github.sds100.keymapper.base.keymaps.PauseKeyMapsUseCase import io.github.sds100.keymapper.base.keymaps.TriggerKeyMapEvent -import io.github.sds100.keymapper.base.promode.SystemBridgeSetupAssistantController import io.github.sds100.keymapper.base.system.inputmethod.AutoSwitchImeController import io.github.sds100.keymapper.base.trigger.RecordTriggerController import io.github.sds100.keymapper.common.utils.Constants @@ -82,7 +82,6 @@ abstract class BaseAccessibilityServiceController( private val detectKeyMapsUseCase = detectKeyMapsUseCaseFactory.create( accessibilityService = service, - coroutineScope = service.lifecycleScope, ) val detectConstraintsUseCase = detectConstraintsUseCaseFactory.create(service) @@ -95,6 +94,7 @@ abstract class BaseAccessibilityServiceController( inputEventHub, pauseKeyMapsUseCase, recordTriggerController, + settingsRepository, ) val triggerKeyMapFromOtherAppsController = TriggerKeyMapFromOtherAppsController( @@ -462,7 +462,9 @@ abstract class BaseAccessibilityServiceController( } is AccessibilityServiceEvent.HideKeyboard -> service.hideKeyboard() + is AccessibilityServiceEvent.ShowKeyboard -> service.showKeyboard() + is AccessibilityServiceEvent.ChangeIme -> if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { service.switchIme(event.imeId) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/inputmethod/ImeInputEventInjector.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/inputmethod/ImeInputEventInjector.kt index 41272fea50..7946bf53bd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/inputmethod/ImeInputEventInjector.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/inputmethod/ImeInputEventInjector.kt @@ -92,7 +92,7 @@ class ImeInputEventInjectorImpl @Inject constructor( // Otherwise, revert to the special key event containing // the actual characters. val event = KeyEvent( - SystemClock.uptimeMillis(), + SystemClock.elapsedRealtime(), text, KeyCharacterMap.VIRTUAL_KEYBOARD, 0, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt index 3fa824beee..31054d496d 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/notifications/NotificationController.kt @@ -106,7 +106,7 @@ class NotificationController @Inject constructor( manageNotifications.createChannel( NotificationChannelModel( id = CHANNEL_SETUP_ASSISTANT, - name = getString(R.string.pro_mode_setup_assistant_notification_channel), + name = getString(R.string.expert_mode_setup_assistant_notification_channel), importance = NotificationManagerCompat.IMPORTANCE_MAX, ), ) @@ -162,16 +162,21 @@ class NotificationController @Inject constructor( manageNotifications.onActionClick.onEach { actionId -> when (actionId) { KMNotificationAction.IntentAction.RESUME_KEY_MAPS -> pauseMappings.resume() + KMNotificationAction.IntentAction.PAUSE_KEY_MAPS -> pauseMappings.pause() + KMNotificationAction.IntentAction.DISMISS_TOGGLE_KEY_MAPS_NOTIFICATION -> manageNotifications.dismiss(ID_TOGGLE_MAPPINGS) KMNotificationAction.IntentAction.STOP_ACCESSIBILITY_SERVICE -> controlAccessibilityService.stopService() + KMNotificationAction.IntentAction.START_ACCESSIBILITY_SERVICE -> attemptStartAccessibilityService() + KMNotificationAction.IntentAction.RESTART_ACCESSIBILITY_SERVICE -> attemptRestartAccessibilityService() + KMNotificationAction.IntentAction.TOGGLE_KEY_MAPPER_IME -> toggleCompatibleIme.toggle() .onSuccess { @@ -181,6 +186,7 @@ class NotificationController @Inject constructor( } KMNotificationAction.IntentAction.SHOW_KEYBOARD -> hideInputMethod.show() + else -> Unit // Ignore other notification actions } }.launchIn(coroutineScope) @@ -416,10 +422,10 @@ class NotificationController @Inject constructor( private fun showSystemBridgeStartedNotification() { val model = NotificationModel( id = ID_SYSTEM_BRIDGE_STATUS, - title = getString(R.string.pro_mode_setup_notification_system_bridge_started_title), - text = getString(R.string.pro_mode_setup_notification_system_bridge_started_text), + title = getString(R.string.expert_mode_setup_notification_system_bridge_started_title), + text = getString(R.string.expert_mode_setup_notification_system_bridge_started_text), channel = CHANNEL_SETUP_ASSISTANT, - icon = R.drawable.pro_mode, + icon = R.drawable.offline_bolt_24px, onGoing = false, showOnLockscreen = false, autoCancel = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt index 89f521832f..9fce955b46 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/system/permissions/RequestPermissionDelegate.kt @@ -68,24 +68,35 @@ class RequestPermissionDelegate( fun requestPermission(permission: Permission) { when (permission) { Permission.WRITE_SETTINGS -> requestWriteSettings() + Permission.CAMERA -> requestPermissionLauncher.launch(Manifest.permission.CAMERA) + Permission.DEVICE_ADMIN -> requestDeviceAdmin() + Permission.READ_PHONE_STATE -> requestPermissionLauncher.launch( Manifest.permission.READ_PHONE_STATE, ) + Permission.ACCESS_NOTIFICATION_POLICY -> requestAccessNotificationPolicy() + Permission.WRITE_SECURE_SETTINGS -> requestWriteSecureSettings() + Permission.NOTIFICATION_LISTENER -> notificationReceiverAdapter.start() + Permission.CALL_PHONE -> requestPermissionLauncher.launch( Manifest.permission.CALL_PHONE, ) + Permission.SEND_SMS -> requestPermissionLauncher.launch(Manifest.permission.SEND_SMS) + Permission.ANSWER_PHONE_CALL -> requestPermissionLauncher.launch( Manifest.permission.ANSWER_PHONE_CALLS, ) + Permission.FIND_NEARBY_DEVICES -> requestPermissionLauncher.launch( Manifest.permission.BLUETOOTH_CONNECT, ) + Permission.ROOT -> requestRootPermission() Permission.IGNORE_BATTERY_OPTIMISATION -> @@ -176,11 +187,11 @@ class RequestPermissionDelegate( messageResource = R.string.dialog_message_write_secure_settings positiveButton(R.string.pos_proceed) { - val destination = NavDestination.ProMode + val destination = NavDestination.ExpertMode coroutineScope.launch { navigationProvider.navigate( - "grant_write_secure_settings_pro_mode", + "grant_write_secure_settings_expert_mode", destination, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt index b94322819c..e55d4413b8 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseConfigTriggerViewModel.kt @@ -80,14 +80,14 @@ abstract class BaseConfigTriggerViewModel( private const val DEVICE_ID_ANY = "any" private const val DEVICE_ID_INTERNAL = "internal" - fun buildProModeSwitchState( + fun buildExpertModeSwitchState( recordTriggerState: RecordTriggerState, - isProModeRecordingEnabled: Boolean, + isExpertModeRecordingEnabled: Boolean, systemBridgeState: SystemBridgeConnectionState, - ): ProModeRecordSwitchState { - return ProModeRecordSwitchState( + ): ExpertModeRecordSwitchState { + return ExpertModeRecordSwitchState( isVisible = systemBridgeState is SystemBridgeConnectionState.Connected, - isChecked = isProModeRecordingEnabled, + isChecked = isExpertModeRecordingEnabled, isEnabled = recordTriggerState !is RecordTriggerState.CountingDown, ) } @@ -112,18 +112,18 @@ abstract class BaseConfigTriggerViewModel( RecordTriggerState.Idle, ) - val proModeSwitchState: StateFlow = + val expertModeSwitchState: StateFlow = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { combine( recordTrigger.state, recordTrigger.isEvdevRecordingEnabled, systemBridgeConnectionManager.connectionState, - Companion::buildProModeSwitchState, + Companion::buildExpertModeSwitchState, ) .stateIn( viewModelScope, SharingStarted.Eagerly, - ProModeRecordSwitchState( + ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = false, @@ -131,7 +131,11 @@ abstract class BaseConfigTriggerViewModel( ) } else { MutableStateFlow( - ProModeRecordSwitchState(isVisible = false, isChecked = false, isEnabled = false), + ExpertModeRecordSwitchState( + isVisible = false, + isChecked = false, + isEnabled = false, + ), ) } @@ -298,6 +302,7 @@ abstract class BaseConfigTriggerViewModel( when (keyMapState) { State.Loading -> return null + is State.Data -> { val trigger = keyMapState.data.trigger val key = trigger.keys.find { it.uid == triggerKeyUid } @@ -478,7 +483,9 @@ abstract class BaseConfigTriggerViewModel( triggerKeyOptionsUid.value?.let { triggerKeyUid -> val device = when (descriptor) { DEVICE_ID_ANY -> KeyEventTriggerDevice.Any + DEVICE_ID_INTERNAL -> KeyEventTriggerDevice.Internal + else -> { val device = config.getAvailableTriggerKeyDevices() .filterIsInstance() @@ -537,7 +544,7 @@ abstract class BaseConfigTriggerViewModel( } } - fun onProModeSwitchChange(isChecked: Boolean) = + fun onExpertModeSwitchChange(isChecked: Boolean) = recordTrigger.setEvdevRecordingEnabled(isChecked) fun handleServiceEventResult(result: KMResult<*>) { @@ -549,11 +556,11 @@ abstract class BaseConfigTriggerViewModel( override fun onTipButtonClick(tipId: String) { when (tipId) { OnboardingTipDelegateImpl.CAPS_LOCK_PRO_MODE_COMPATIBILITY_TIP_ID -> { - showTriggerSetup(TriggerSetupShortcut.KEYBOARD, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.KEYBOARD, forceExpertMode = true) } OnboardingTipDelegateImpl.VOLUME_BUTTONS_PRO_MODE_TIP_ID -> { - showTriggerSetup(TriggerSetupShortcut.VOLUME, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.VOLUME, forceExpertMode = true) } } } @@ -587,7 +594,7 @@ abstract class BaseConfigTriggerViewModel( val response = showDialog("migrate_screen_off", dialog) if (response == DialogResponse.POSITIVE) { - showTriggerSetup(TriggerSetupShortcut.OTHER, forceProMode = true) + showTriggerSetup(TriggerSetupShortcut.OTHER, forceExpertMode = true) } } @@ -715,7 +722,9 @@ abstract class BaseConfigTriggerViewModel( showDeviceDescriptors: Boolean, ): String = when (device) { is KeyEventTriggerDevice.Internal -> getString(R.string.this_device) + is KeyEventTriggerDevice.Any -> getString(R.string.any_device) + is KeyEventTriggerDevice.External -> { if (showDeviceDescriptors) { InputDeviceUtils.appendDeviceDescriptorToName( diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt index fa33295b6c..6be1f4e4a4 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt @@ -59,7 +59,7 @@ fun BaseTriggerScreen( val scope = rememberCoroutineScope() val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val recordTriggerState by viewModel.recordTriggerState.collectAsStateWithLifecycle() - val proModeSwitchState by viewModel.proModeSwitchState.collectAsStateWithLifecycle() + val expertModeSwitchState by viewModel.expertModeSwitchState.collectAsStateWithLifecycle() val showFingerprintGestures: Boolean by viewModel.showFingerprintGesturesShortcut.collectAsStateWithLifecycle() @@ -110,6 +110,7 @@ fun BaseTriggerScreen( when (val state = configState) { is State.Loading -> Loading(modifier = modifier) + is State.Data -> { val tipContent: @Composable () -> Unit = { tipModel?.let { tip -> @@ -134,11 +135,11 @@ fun BaseTriggerScreen( modifier = modifier, configState = state.data, recordTriggerState = recordTriggerState, - proModeSwitchState = proModeSwitchState, + expertModeSwitchState = expertModeSwitchState, onRemoveClick = viewModel::onRemoveKeyClick, onEditClick = viewModel::onTriggerKeyOptionsClick, onRecordTriggerClick = viewModel::onRecordTriggerButtonClick, - onProModeSwitchChange = viewModel::onProModeSwitchChange, + onExpertModeSwitchChange = viewModel::onExpertModeSwitchChange, onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick, onSelectClickType = viewModel::onClickTypeRadioButtonChecked, onSelectParallelMode = viewModel::onParallelRadioButtonChecked, @@ -156,11 +157,11 @@ fun BaseTriggerScreen( modifier = modifier, configState = state.data, recordTriggerState = recordTriggerState, - proModeSwitchState = proModeSwitchState, + expertModeSwitchState = expertModeSwitchState, onRemoveClick = viewModel::onRemoveKeyClick, onEditClick = viewModel::onTriggerKeyOptionsClick, onRecordTriggerClick = viewModel::onRecordTriggerButtonClick, - onProModeSwitchChange = viewModel::onProModeSwitchChange, + onExpertModeSwitchChange = viewModel::onExpertModeSwitchChange, onAdvancedTriggersClick = viewModel::onAdvancedTriggersClick, onSelectClickType = viewModel::onClickTypeRadioButtonChecked, onSelectParallelMode = viewModel::onParallelRadioButtonChecked, @@ -205,14 +206,14 @@ private fun TriggerScreenVertical( modifier: Modifier = Modifier, configState: ConfigTriggerState, recordTriggerState: RecordTriggerState, - proModeSwitchState: ProModeRecordSwitchState, + expertModeSwitchState: ExpertModeRecordSwitchState, onRemoveClick: (String) -> Unit = {}, onEditClick: (String) -> Unit = {}, onSelectClickType: (ClickType) -> Unit = {}, onSelectParallelMode: () -> Unit = {}, onSelectSequenceMode: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onProModeSwitchChange: (Boolean) -> Unit = {}, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> }, onFixErrorClick: (TriggerError) -> Unit = {}, @@ -239,8 +240,8 @@ private fun TriggerScreenVertical( modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -301,8 +302,8 @@ private fun TriggerScreenVertical( .padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -316,14 +317,14 @@ private fun TriggerScreenHorizontal( modifier: Modifier = Modifier, configState: ConfigTriggerState, recordTriggerState: RecordTriggerState, - proModeSwitchState: ProModeRecordSwitchState, + expertModeSwitchState: ExpertModeRecordSwitchState, onRemoveClick: (String) -> Unit = {}, onEditClick: (String) -> Unit = {}, onSelectClickType: (ClickType) -> Unit = {}, onSelectParallelMode: () -> Unit = {}, onSelectSequenceMode: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onProModeSwitchChange: (Boolean) -> Unit = {}, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, onMoveTriggerKey: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> }, onFixErrorClick: (TriggerError) -> Unit = {}, @@ -349,8 +350,8 @@ private fun TriggerScreenHorizontal( .padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -418,8 +419,8 @@ private fun TriggerScreenHorizontal( modifier = Modifier.padding(start = 8.dp, end = 8.dp, bottom = 8.dp), onRecordTriggerClick = onRecordTriggerClick, recordTriggerState = recordTriggerState, - proModeRecordSwitchState = proModeSwitchState, - onProModeSwitchChange = onProModeSwitchChange, + expertModeRecordSwitchState = expertModeSwitchState, + onExpertModeSwitchChange = onExpertModeSwitchChange, onAdvancedTriggersClick = onAdvancedTriggersClick, ) } @@ -625,7 +626,7 @@ private fun VerticalPreview() { TriggerScreenVertical( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -644,7 +645,7 @@ private fun VerticalPreviewTiny() { TriggerScreenVertical( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -663,7 +664,7 @@ private fun VerticalEmptyPreview() { TriggerScreenVertical( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = true, @@ -682,7 +683,7 @@ private fun VerticalEmptyDarkPreview() { TriggerScreenVertical( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = false, @@ -701,7 +702,7 @@ private fun HorizontalPreview() { TriggerScreenHorizontal( configState = previewState, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -735,7 +736,7 @@ private fun HorizontalEmptyPreview() { TriggerScreenHorizontal( configState = ConfigTriggerState.Empty, recordTriggerState = RecordTriggerState.Idle, - proModeSwitchState = ProModeRecordSwitchState( + expertModeSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt similarity index 77% rename from base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt rename to base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt index 37d2add993..6f43f789be 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/ProModeRecordSwitchState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/ExpertModeRecordSwitchState.kt @@ -1,6 +1,6 @@ package io.github.sds100.keymapper.base.trigger -data class ProModeRecordSwitchState( +data class ExpertModeRecordSwitchState( val isVisible: Boolean, val isChecked: Boolean, val isEnabled: Boolean, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/KeyEventTriggerKey.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/KeyEventTriggerKey.kt index c5b5f20d9f..ebd8104ea8 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/KeyEventTriggerKey.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/KeyEventTriggerKey.kt @@ -4,7 +4,6 @@ import io.github.sds100.keymapper.base.keymaps.ClickType import io.github.sds100.keymapper.common.utils.hasFlag import io.github.sds100.keymapper.common.utils.withFlag import io.github.sds100.keymapper.data.entities.KeyEventTriggerKeyEntity -import io.github.sds100.keymapper.data.entities.TriggerEntity import io.github.sds100.keymapper.data.entities.TriggerKeyEntity import java.util.UUID import kotlinx.serialization.Serializable @@ -75,7 +74,7 @@ data class KeyEventTriggerKey( entity.flags.hasFlag(KeyEventTriggerKeyEntity.FLAG_DETECTION_SOURCE_INPUT_METHOD) val detectWithScancode = - entity.flags.hasFlag(TriggerEntity.TRIGGER_FLAG_VIBRATE) + entity.flags.hasFlag(KeyEventTriggerKeyEntity.FLAG_DETECT_WITH_SCAN_CODE) return KeyEventTriggerKey( uid = entity.uid, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt index 64bff3dd91..b58c74cf73 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerButtonRow.kt @@ -19,6 +19,7 @@ import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.text.BasicText import androidx.compose.foundation.text.TextAutoSize import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.outlined.ShoppingCart import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.FilledTonalButton @@ -42,25 +43,22 @@ import androidx.compose.ui.unit.sp 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.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIconDisabled @Composable fun RecordTriggerButtonRow( modifier: Modifier = Modifier, onRecordTriggerClick: () -> Unit = {}, recordTriggerState: RecordTriggerState, - proModeRecordSwitchState: ProModeRecordSwitchState, - onProModeSwitchChange: (Boolean) -> Unit = {}, + expertModeRecordSwitchState: ExpertModeRecordSwitchState, + onExpertModeSwitchChange: (Boolean) -> Unit = {}, onAdvancedTriggersClick: () -> Unit = {}, ) { Column(modifier = modifier) { Row(verticalAlignment = Alignment.CenterVertically) { - if (proModeRecordSwitchState.isVisible) { - ProModeSwitch( - state = proModeRecordSwitchState, - onCheckedChange = onProModeSwitchChange, + if (expertModeRecordSwitchState.isVisible) { + ExpertModeSwitch( + state = expertModeRecordSwitchState, + onCheckedChange = onExpertModeSwitchChange, ) Spacer(modifier = Modifier.width(8.dp)) @@ -69,6 +67,7 @@ fun RecordTriggerButtonRow( RecordTriggerButton( modifier = Modifier.weight(1f), recordTriggerState, + isExpertModeRecordingEnabled = expertModeRecordSwitchState.isChecked, onClick = onRecordTriggerClick, ) @@ -80,9 +79,9 @@ fun RecordTriggerButtonRow( } @Composable -private fun ProModeSwitch( +private fun ExpertModeSwitch( modifier: Modifier = Modifier, - state: ProModeRecordSwitchState, + state: ExpertModeRecordSwitchState, onCheckedChange: (Boolean) -> Unit, ) { Switch( @@ -99,11 +98,7 @@ private fun ProModeSwitch( thumbContent = { Icon( modifier = Modifier.padding(2.dp), - imageVector = if (state.isChecked) { - KeyMapperIcons.ProModeIcon - } else { - KeyMapperIcons.ProModeIconDisabled - }, + imageVector = Icons.Outlined.OfflineBolt, contentDescription = null, ) }, @@ -111,7 +106,12 @@ private fun ProModeSwitch( } @Composable -fun RecordTriggerButton(modifier: Modifier, state: RecordTriggerState, onClick: () -> Unit) { +fun RecordTriggerButton( + modifier: Modifier, + state: RecordTriggerState, + isExpertModeRecordingEnabled: Boolean, + onClick: () -> Unit, +) { val colors = ButtonDefaults.filledTonalButtonColors().copy( containerColor = LocalCustomColorsPalette.current.red, contentColor = LocalCustomColorsPalette.current.onRed, @@ -121,8 +121,11 @@ fun RecordTriggerButton(modifier: Modifier, state: RecordTriggerState, onClick: is RecordTriggerState.CountingDown -> stringResource(R.string.button_recording_trigger_countdown, state.timeLeft) - else -> + else -> if (isExpertModeRecordingEnabled) { + stringResource(R.string.button_record_trigger_expert_mode) + } else { stringResource(R.string.button_record_trigger) + } } // Create pulsing animation for the recording dot @@ -196,7 +199,7 @@ private fun PreviewCountingDown() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.CountingDown(3), - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -214,7 +217,7 @@ private fun PreviewStopped() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -232,7 +235,7 @@ private fun PreviewStoppedDark() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = true, @@ -250,7 +253,7 @@ private fun PreviewStoppedCompact() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, @@ -262,13 +265,13 @@ private fun PreviewStoppedCompact() { @Preview(widthDp = 400) @Composable -private fun PreviewProModeSwitchHidden() { +private fun PreviewExpertModeSwitchHidden() { KeyMapperTheme { Surface { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = false, isChecked = false, isEnabled = true, @@ -280,13 +283,13 @@ private fun PreviewProModeSwitchHidden() { @Preview(widthDp = 400) @Composable -private fun PreviewProModeSwitchDisabled() { +private fun PreviewExpertModeSwitchDisabled() { KeyMapperTheme { Surface { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Idle, - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = true, isEnabled = false, @@ -304,7 +307,7 @@ private fun PreviewCompleted() { RecordTriggerButtonRow( modifier = Modifier.fillMaxWidth(), recordTriggerState = RecordTriggerState.Completed(emptyList()), - proModeRecordSwitchState = ProModeRecordSwitchState( + expertModeRecordSwitchState = ExpertModeRecordSwitchState( isVisible = true, isChecked = false, isEnabled = true, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt index 725ae003f7..b8e2b97424 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordTriggerController.kt @@ -223,7 +223,7 @@ class RecordTriggerControllerImpl @Inject constructor( return RecordedKey.EvdevEvent( keyCode = evdevEvent.androidCode, scanCode = evdevEvent.code, - device = evdevEvent.device, + device = evdevEvent.deviceInfo, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt index 468b64a329..b6e611b9c1 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/RecordedKey.kt @@ -1,7 +1,7 @@ package io.github.sds100.keymapper.base.trigger import io.github.sds100.keymapper.base.input.InputEventDetectionSource -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo sealed class RecordedKey { data class KeyEvent( @@ -13,6 +13,6 @@ sealed class RecordedKey { val detectionSource: InputEventDetectionSource, ) : RecordedKey() - data class EvdevEvent(val keyCode: Int, val scanCode: Int, val device: EvdevDeviceHandle) : + data class EvdevEvent(val keyCode: Int, val scanCode: Int, val device: EvdevDeviceInfo) : RecordedKey() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt index 3fb455de52..51af4cc2fb 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerKeyListItem.kt @@ -18,6 +18,7 @@ import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.Assistant import androidx.compose.material.icons.outlined.BubbleChart import androidx.compose.material.icons.outlined.Fingerprint +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.outlined.Settings import androidx.compose.material.icons.rounded.Add import androidx.compose.material.icons.rounded.ArrowDownward @@ -46,8 +47,6 @@ import io.github.sds100.keymapper.base.keymaps.ClickType import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType import io.github.sds100.keymapper.base.utils.ui.LinkType import io.github.sds100.keymapper.base.utils.ui.compose.DragDropState -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @Composable fun TriggerKeyListItem( @@ -114,7 +113,7 @@ fun TriggerKeyListItem( is TriggerKeyListItemModel.Assistant -> Icons.Outlined.Assistant is TriggerKeyListItemModel.FloatingButton -> Icons.Outlined.BubbleChart is TriggerKeyListItemModel.FingerprintGesture -> Icons.Outlined.Fingerprint - is TriggerKeyListItemModel.EvdevEvent -> KeyMapperIcons.ProModeIcon + is TriggerKeyListItemModel.EvdevEvent -> Icons.Outlined.OfflineBolt else -> null } @@ -133,9 +132,11 @@ fun TriggerKeyListItem( AssistantTriggerType.ANY -> stringResource( R.string.assistant_any_trigger_name, ) + AssistantTriggerType.VOICE -> stringResource( R.string.assistant_voice_trigger_name, ) + AssistantTriggerType.DEVICE -> stringResource( R.string.assistant_device_trigger_name, ) @@ -147,6 +148,7 @@ fun TriggerKeyListItem( ) is TriggerKeyListItemModel.KeyEvent -> model.keyName + is TriggerKeyListItemModel.EvdevEvent -> model.keyName is TriggerKeyListItemModel.FloatingButtonDeleted -> stringResource( @@ -157,12 +159,15 @@ fun TriggerKeyListItem( FingerprintGestureType.SWIPE_UP -> stringResource( R.string.trigger_key_fingerprint_gesture_up, ) + FingerprintGestureType.SWIPE_DOWN -> stringResource( R.string.trigger_key_fingerprint_gesture_down, ) + FingerprintGestureType.SWIPE_LEFT -> stringResource( R.string.trigger_key_fingerprint_gesture_left, ) + FingerprintGestureType.SWIPE_RIGHT -> stringResource( R.string.trigger_key_fingerprint_gesture_right, ) @@ -182,7 +187,6 @@ fun TriggerKeyListItem( is TriggerKeyListItemModel.KeyEvent -> model.extraInfo is TriggerKeyListItemModel.EvdevEvent -> model.extraInfo is TriggerKeyListItemModel.FloatingButton -> model.layoutName - else -> null } @@ -278,33 +282,43 @@ fun TriggerKeyListItem( private fun getErrorMessage(error: TriggerError): String { return when (error) { TriggerError.DND_ACCESS_DENIED -> stringResource(R.string.trigger_error_dnd_access_denied) + TriggerError.CANT_DETECT_IN_PHONE_CALL -> stringResource( R.string.trigger_error_cant_detect_in_phone_call, ) + TriggerError.ASSISTANT_TRIGGER_NOT_PURCHASED -> stringResource( R.string.trigger_error_assistant_not_purchased, ) + TriggerError.DPAD_IME_NOT_SELECTED -> stringResource( R.string.trigger_error_dpad_ime_not_selected, ) + TriggerError.FLOATING_BUTTON_DELETED -> stringResource( R.string.trigger_error_floating_button_deleted, ) + TriggerError.FLOATING_BUTTONS_NOT_PURCHASED -> stringResource( R.string.trigger_error_floating_buttons_not_purchased, ) + TriggerError.PURCHASE_VERIFICATION_FAILED -> stringResource( R.string.trigger_error_product_verification_failed, ) + TriggerError.SYSTEM_BRIDGE_UNSUPPORTED -> stringResource( R.string.trigger_error_system_bridge_unsupported, ) + TriggerError.SYSTEM_BRIDGE_DISCONNECTED -> stringResource( R.string.trigger_error_system_bridge_disconnected, ) + TriggerError.EVDEV_DEVICE_NOT_FOUND -> stringResource( R.string.trigger_error_evdev_device_not_found, ) + TriggerError.MIGRATE_SCREEN_OFF_TRIGGER -> stringResource( R.string.trigger_error_migrate_screen_off_key_map, ) diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt index 5aaf8bdefa..c00cb0f19b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupBottomSheet.kt @@ -52,13 +52,13 @@ 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.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus import io.github.sds100.keymapper.base.utils.ui.compose.AccessibilityServiceRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.CheckBoxText +import io.github.sds100.keymapper.base.utils.ui.compose.ExpertModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.HeaderText import io.github.sds100.keymapper.base.utils.ui.compose.InputMethodRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.KeyMapperSegmentedButtonRow -import io.github.sds100.keymapper.base.utils.ui.compose.ProModeRequirementRow import io.github.sds100.keymapper.base.utils.ui.compose.RadioButtonText import io.github.sds100.keymapper.base.utils.ui.compose.icons.IndeterminateQuestionBox import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons @@ -79,9 +79,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Volume, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.Power -> PowerTriggerSetupBottomSheet( @@ -89,7 +89,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Power, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -107,9 +107,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Keyboard, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.Mouse -> MouseTriggerSetupBottomSheet( @@ -117,7 +117,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Mouse, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -126,9 +126,9 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.Other, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, ) is TriggerSetupState.NotDetected -> NotDetectedSetupBottomSheet( @@ -136,7 +136,7 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { state = triggerSetupState as TriggerSetupState.NotDetected, onDismissRequest = delegate::onDismissTriggerSetup, onEnableAccessibilityServiceClick = delegate::onEnableAccessibilityServiceClick, - onEnableProModeClick = delegate::onEnableProModeClick, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, onRecordTriggerClick = delegate::onTriggerSetupRecordClick, ) @@ -149,8 +149,8 @@ fun HandleTriggerSetupBottomSheet(delegate: TriggerSetupDelegate) { onRecordTriggerClick = delegate::onTriggerSetupRecordClick, onEnableInputMethodClick = delegate::onEnableImeClick, onChooseInputMethodClick = delegate::onChooseImeClick, - onUseProModeCheckedChange = delegate::onUseProModeCheckedChange, - onEnableProModeClick = delegate::onEnableProModeClick, + onUseExpertModeCheckedChange = delegate::onUseExpertModeCheckedChange, + onEnableExpertModeClick = delegate::onEnableExpertModeClick, ) null -> {} @@ -167,10 +167,10 @@ private fun GamepadTriggerSetupBottomSheet( onRecordTriggerClick: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, onSelectButtonType: (TriggerSetupState.Gamepad.Type) -> Unit = { }, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onEnableInputMethodClick: () -> Unit = { }, onChooseInputMethodClick: () -> Unit = { }, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -183,6 +183,9 @@ private fun GamepadTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = + state is TriggerSetupState.Gamepad.SimpleButtons && + state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -208,6 +211,7 @@ private fun GamepadTriggerSetupBottomSheet( val selectedState = when (state) { is TriggerSetupState.Gamepad.Dpad -> TriggerSetupState.Gamepad.Type.DPAD + is TriggerSetupState.Gamepad.SimpleButtons -> TriggerSetupState.Gamepad.Type.SIMPLE_BUTTONS } @@ -219,12 +223,12 @@ private fun GamepadTriggerSetupBottomSheet( onStateSelected = onSelectButtonType, ) - val isUseProModeChecked = when (state) { + val isUseExpertModeChecked = when (state) { is TriggerSetupState.Gamepad.Dpad -> false - is TriggerSetupState.Gamepad.SimpleButtons -> state.isUseProModeChecked + is TriggerSetupState.Gamepad.SimpleButtons -> state.isUseExpertModeChecked } - val isUseProModeEnabled = when (state) { + val isUseExpertModeEnabled = when (state) { is TriggerSetupState.Gamepad.Dpad -> false is TriggerSetupState.Gamepad.SimpleButtons -> true } @@ -232,9 +236,9 @@ private fun GamepadTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = isUseProModeChecked, - isEnabled = isUseProModeEnabled, - onCheckedChange = onUseProModeCheckedChange, + isChecked = isUseExpertModeChecked, + isEnabled = isUseExpertModeEnabled, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -256,11 +260,11 @@ private fun GamepadTriggerSetupBottomSheet( } is TriggerSetupState.Gamepad.SimpleButtons -> { - ProModeRequirementRow( + ExpertModeRequirementRow( modifier = Modifier.fillMaxWidth(), - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -283,7 +287,7 @@ private fun MouseTriggerSetupBottomSheet( state: TriggerSetupState.Mouse, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -297,6 +301,7 @@ private fun MouseTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -323,10 +328,10 @@ private fun MouseTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -338,7 +343,7 @@ private fun PowerTriggerSetupBottomSheet( state: TriggerSetupState.Power, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -352,6 +357,7 @@ private fun PowerTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -378,10 +384,10 @@ private fun PowerTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -423,9 +429,9 @@ private fun VolumeTriggerSetupBottomSheet( state: TriggerSetupState.Volume, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -439,6 +445,7 @@ private fun VolumeTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -457,9 +464,9 @@ private fun VolumeTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -469,10 +476,10 @@ private fun VolumeTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -484,7 +491,7 @@ private fun NotDetectedSetupBottomSheet( state: TriggerSetupState.NotDetected, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, ) { TriggerSetupBottomSheet( @@ -499,6 +506,7 @@ private fun NotDetectedSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = true, onClick = onRecordTriggerClick, ) } else { @@ -531,10 +539,10 @@ private fun NotDetectedSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( + ExpertModeRequirementRow( isVisible = true, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -569,9 +577,9 @@ private fun OtherTriggerSetupBottomSheet( state: TriggerSetupState.Other, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -585,6 +593,7 @@ private fun OtherTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -603,9 +612,9 @@ private fun OtherTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -615,10 +624,10 @@ private fun OtherTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) HeaderText(text = stringResource(R.string.trigger_setup_information_title)) @@ -653,9 +662,9 @@ private fun KeyboardTriggerSetupBottomSheet( state: TriggerSetupState.Keyboard, onDismissRequest: () -> Unit = {}, onEnableAccessibilityServiceClick: () -> Unit = {}, - onEnableProModeClick: () -> Unit = {}, + onEnableExpertModeClick: () -> Unit = {}, onRecordTriggerClick: () -> Unit = {}, - onUseProModeCheckedChange: (Boolean) -> Unit = {}, + onUseExpertModeCheckedChange: (Boolean) -> Unit = {}, ) { TriggerSetupBottomSheet( modifier = modifier, @@ -669,6 +678,7 @@ private fun KeyboardTriggerSetupBottomSheet( RecordTriggerButton( modifier = Modifier.weight(1f), state = state.recordTriggerState, + isExpertModeRecordingEnabled = state.isUseExpertModeChecked, onClick = onRecordTriggerClick, ) } else { @@ -687,9 +697,9 @@ private fun KeyboardTriggerSetupBottomSheet( CheckBoxText( modifier = Modifier.fillMaxWidth(), text = stringResource(R.string.trigger_setup_screen_off_option), - isChecked = state.isUseProModeChecked, - isEnabled = !state.forceProMode, - onCheckedChange = onUseProModeCheckedChange, + isChecked = state.isUseExpertModeChecked, + isEnabled = !state.forceExpertMode, + onCheckedChange = onUseExpertModeCheckedChange, ) HeaderText(text = stringResource(R.string.trigger_setup_requirements_title)) @@ -699,10 +709,10 @@ private fun KeyboardTriggerSetupBottomSheet( onClick = onEnableAccessibilityServiceClick, ) - ProModeRequirementRow( - isVisible = state.isUseProModeChecked, - proModeStatus = state.proModeStatus, - onClick = onEnableProModeClick, + ExpertModeRequirementRow( + isVisible = state.isUseExpertModeChecked, + expertModeStatus = state.expertModeStatus, + onClick = onEnableExpertModeClick, ) } } @@ -910,7 +920,7 @@ private fun PowerButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Power( isAccessibilityServiceEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.SUPPORTED, @@ -934,7 +944,7 @@ private fun PowerButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Power( isAccessibilityServiceEnabled = false, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.UNSUPPORTED, @@ -958,11 +968,11 @@ private fun VolumeButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Volume( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -983,11 +993,11 @@ private fun VolumeButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Volume( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1052,11 +1062,11 @@ private fun KeyboardButtonEnabledPreview() { sheetState = sheetState, state = TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = true, - isUseProModeChecked = false, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = false, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1077,11 +1087,11 @@ private fun KeyboardButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1102,7 +1112,7 @@ private fun MouseButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Mouse( isAccessibilityServiceEnabled = true, - proModeStatus = ProModeStatus.ENABLED, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.SUPPORTED, @@ -1126,7 +1136,7 @@ private fun MouseButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Mouse( isAccessibilityServiceEnabled = false, - proModeStatus = ProModeStatus.UNSUPPORTED, + expertModeStatus = ExpertModeStatus.UNSUPPORTED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, remapStatus = RemapStatus.UNSUPPORTED, @@ -1150,11 +1160,11 @@ private fun OtherButtonPreview() { sheetState = sheetState, state = TriggerSetupState.Other( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1175,11 +1185,11 @@ private fun OtherButtonDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Other( isAccessibilityServiceEnabled = false, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1250,11 +1260,11 @@ private fun GamepadSimpleButtonsPreview() { sheetState = sheetState, state = TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = true, - isUseProModeChecked = true, - proModeStatus = ProModeStatus.ENABLED, + isUseExpertModeChecked = true, + expertModeStatus = ExpertModeStatus.ENABLED, areRequirementsMet = true, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } @@ -1275,11 +1285,11 @@ private fun GamepadSimpleButtonsDisabledPreview() { sheetState = sheetState, state = TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = false, - isUseProModeChecked = false, - proModeStatus = ProModeStatus.DISABLED, + isUseExpertModeChecked = false, + expertModeStatus = ExpertModeStatus.DISABLED, areRequirementsMet = false, recordTriggerState = RecordTriggerState.Idle, - forceProMode = false, + forceExpertMode = false, ), ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt index c5af7ca363..9eaec3e10c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupDelegate.kt @@ -4,7 +4,7 @@ import android.os.Build import dagger.hilt.android.scopes.ViewModelScoped import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus 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 @@ -58,8 +58,8 @@ class TriggerSetupDelegateImpl @Inject constructor( MutableStateFlow(null) private val isScreenOffChecked: MutableStateFlow = MutableStateFlow(false) - private val isProModeLocked: MutableStateFlow = MutableStateFlow(false) - private val forceProMode: MutableStateFlow = MutableStateFlow(false) + private val isExpertModeLocked: MutableStateFlow = MutableStateFlow(false) + private val forceExpertMode: MutableStateFlow = MutableStateFlow(false) private val selectedFingerprintGestureType: MutableStateFlow = MutableStateFlow(FingerprintGestureType.SWIPE_DOWN) @@ -67,16 +67,16 @@ class TriggerSetupDelegateImpl @Inject constructor( private val selectedGamePadType: MutableStateFlow = MutableStateFlow(TriggerSetupState.Gamepad.Type.DPAD) - private val proModeStatus: Flow = + private val expertModeStatus: Flow = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { systemBridgeConnectionManager.connectionState.map { state -> when (state) { - is SystemBridgeConnectionState.Connected -> ProModeStatus.ENABLED - is SystemBridgeConnectionState.Disconnected -> ProModeStatus.DISABLED + is SystemBridgeConnectionState.Connected -> ExpertModeStatus.ENABLED + is SystemBridgeConnectionState.Disconnected -> ExpertModeStatus.DISABLED } } } else { - flowOf(ProModeStatus.UNSUPPORTED) + flowOf(ExpertModeStatus.UNSUPPORTED) } override val triggerSetupState: StateFlow = @@ -93,17 +93,16 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupShortcut.GAMEPAD -> buildSetupGamepadTriggerFlow() TriggerSetupShortcut.OTHER -> buildSetupOtherTriggerFlow() TriggerSetupShortcut.NOT_DETECTED -> buildSetupNotDetectedFlow() - else -> throw UnsupportedOperationException("Unhandled shortcut: $shortcut") } } }.stateIn(viewModelScope, SharingStarted.Lazily, null) - override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceProMode: Boolean) { - isProModeLocked.value = true - this.forceProMode.value = forceProMode + override fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceExpertMode: Boolean) { + isExpertModeLocked.value = true + this.forceExpertMode.value = forceExpertMode // If force pro mode is enabled, automatically check the screen off option - if (forceProMode) { + if (forceExpertMode) { isScreenOffChecked.value = true } currentSetupShortcut.value = shortcut @@ -114,23 +113,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Volume( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -168,18 +173,18 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, + expertModeStatus, + forceExpertMode, ) { serviceState, isScreenOffChecked, recordTriggerState, - proModeStatus, - forceProMode, + expertModeStatus, + forceExpertMode, -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } @@ -187,11 +192,11 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Gamepad.SimpleButtons( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -204,23 +209,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Other( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -229,15 +240,15 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED TriggerSetupState.NotDetected( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, ) @@ -249,23 +260,29 @@ class TriggerSetupDelegateImpl @Inject constructor( accessibilityServiceState, isScreenOffChecked, recordTriggerController.state, - proModeStatus, - forceProMode, - ) { serviceState, isScreenOffChecked, recordTriggerState, proModeStatus, forceProMode -> + expertModeStatus, + forceExpertMode, + ) { + serviceState, + isScreenOffChecked, + recordTriggerState, + expertModeStatus, + forceExpertMode, + -> val areRequirementsMet = if (isScreenOffChecked) { serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED } else { serviceState == AccessibilityServiceState.ENABLED } TriggerSetupState.Keyboard( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - isUseProModeChecked = isScreenOffChecked, - proModeStatus = proModeStatus, + isUseExpertModeChecked = isScreenOffChecked, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, - forceProMode = forceProMode, + forceExpertMode = forceExpertMode, ) } } @@ -289,11 +306,11 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED val remapStatus = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { if (areRequirementsMet) { @@ -307,7 +324,7 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Power( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, remapStatus = remapStatus, @@ -319,11 +336,11 @@ class TriggerSetupDelegateImpl @Inject constructor( return combine( accessibilityServiceState, recordTriggerController.state, - proModeStatus, - ) { serviceState, recordTriggerState, proModeStatus -> + expertModeStatus, + ) { serviceState, recordTriggerState, expertModeStatus -> val areRequirementsMet = serviceState == AccessibilityServiceState.ENABLED && - proModeStatus == ProModeStatus.ENABLED + expertModeStatus == ExpertModeStatus.ENABLED val remapStatus = if (Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) { if (areRequirementsMet) { @@ -337,7 +354,7 @@ class TriggerSetupDelegateImpl @Inject constructor( TriggerSetupState.Mouse( isAccessibilityServiceEnabled = serviceState == AccessibilityServiceState.ENABLED, - proModeStatus = proModeStatus, + expertModeStatus = expertModeStatus, areRequirementsMet = areRequirementsMet, recordTriggerState = recordTriggerState, remapStatus = remapStatus, @@ -351,9 +368,9 @@ class TriggerSetupDelegateImpl @Inject constructor( } } - override fun onEnableProModeClick() { + override fun onEnableExpertModeClick() { viewModelScope.launch { - navigate("trigger_setup_enable_pro_mode", NavDestination.ProMode) + navigate("trigger_setup_enable_expert_mode", NavDestination.ExpertMode) } } @@ -361,7 +378,7 @@ class TriggerSetupDelegateImpl @Inject constructor( isScreenOffChecked.value = isChecked } - override fun onUseProModeCheckedChange(isChecked: Boolean) { + override fun onUseExpertModeCheckedChange(isChecked: Boolean) { isScreenOffChecked.value = isChecked } @@ -373,14 +390,22 @@ class TriggerSetupDelegateImpl @Inject constructor( val setupState = triggerSetupState.value ?: return val enableEvdevRecording = when (setupState) { - is TriggerSetupState.Volume -> setupState.isUseProModeChecked - is TriggerSetupState.Keyboard -> setupState.isUseProModeChecked + is TriggerSetupState.Volume -> setupState.isUseExpertModeChecked + + is TriggerSetupState.Keyboard -> setupState.isUseExpertModeChecked + is TriggerSetupState.Power -> true + is TriggerSetupState.FingerprintGesture -> false + is TriggerSetupState.Mouse -> true - is TriggerSetupState.Other -> setupState.isUseProModeChecked + + is TriggerSetupState.Other -> setupState.isUseExpertModeChecked + is TriggerSetupState.Gamepad.Dpad -> false - is TriggerSetupState.Gamepad.SimpleButtons -> setupState.isUseProModeChecked + + is TriggerSetupState.Gamepad.SimpleButtons -> setupState.isUseExpertModeChecked + // Always enable pro mode recording to increase the chances of detecting // the key is TriggerSetupState.NotDetected -> true @@ -442,12 +467,12 @@ class TriggerSetupDelegateImpl @Inject constructor( interface TriggerSetupDelegate { val triggerSetupState: StateFlow - fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceProMode: Boolean = false) + fun showTriggerSetup(shortcut: TriggerSetupShortcut, forceExpertMode: Boolean = false) fun onDismissTriggerSetup() fun onEnableAccessibilityServiceClick() - fun onEnableProModeClick() + fun onEnableExpertModeClick() fun onScreenOffTriggerSetupCheckedChange(isChecked: Boolean) - fun onUseProModeCheckedChange(isChecked: Boolean) + fun onUseExpertModeCheckedChange(isChecked: Boolean) fun onTriggerSetupRecordClick() fun onFingerprintGestureTypeSelected(type: FingerprintGestureType) fun onAddFingerprintGestureClick() diff --git a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt index e94c8749de..ac7d09402b 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/trigger/TriggerSetupState.kt @@ -1,30 +1,30 @@ package io.github.sds100.keymapper.base.trigger import io.github.sds100.keymapper.base.system.accessibility.FingerprintGestureType -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus sealed class TriggerSetupState { data class Volume( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class Keyboard( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class Power( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, val remapStatus: RemapStatus, @@ -32,7 +32,7 @@ sealed class TriggerSetupState { data class Mouse( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, val remapStatus: RemapStatus, @@ -40,16 +40,16 @@ sealed class TriggerSetupState { data class Other( val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : TriggerSetupState() data class NotDetected( val isAccessibilityServiceEnabled: Boolean, - val proModeStatus: ProModeStatus, + val expertModeStatus: ExpertModeStatus, val areRequirementsMet: Boolean, val recordTriggerState: RecordTriggerState, ) : TriggerSetupState() @@ -75,11 +75,11 @@ sealed class TriggerSetupState { data class SimpleButtons( override val isAccessibilityServiceEnabled: Boolean, - val isUseProModeChecked: Boolean, - val proModeStatus: ProModeStatus, + val isUseExpertModeChecked: Boolean, + val expertModeStatus: ExpertModeStatus, override val areRequirementsMet: Boolean, override val recordTriggerState: RecordTriggerState, - val forceProMode: Boolean = false, + val forceExpertMode: Boolean = false, ) : Gamepad() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt index a9214a7152..72513034f9 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ErrorUtils.kt @@ -455,6 +455,7 @@ fun KMError.getFullMessage(resourceProvider: ResourceProvider): String { 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, @@ -475,6 +476,9 @@ fun KMError.getFullMessage(resourceProvider: ResourceProvider): String { R.string.error_sms_rate_limit, ) + is SystemBridgeError.WriteEvdevEventFailed -> + resourceProvider.getString(R.string.error_write_evdev_event_failed) + else -> this.toString() } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt similarity index 75% rename from base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt rename to base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt index 4bc9c674ec..e927d35e2f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ProModeStatus.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ExpertModeStatus.kt @@ -1,6 +1,6 @@ package io.github.sds100.keymapper.base.utils -enum class ProModeStatus { +enum class ExpertModeStatus { UNSUPPORTED, DISABLED, ENABLED, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt index 0c7f95c588..f13888bccd 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/navigation/NavDestination.kt @@ -42,7 +42,7 @@ abstract class NavDestination(val isCompose: Boolean = false) { const val ID_SHELL_COMMAND_ACTION = "shell_command_action" const val ID_CHOOSE_SETTING = "choose_setting" const val ID_CREATE_NOTIFICATION_ACTION = "create_notification_action" - const val ID_PRO_MODE = "pro_mode" + const val ID_EXPERT_MODE = "expert_mode" const val ID_LOG = "log" const val ID_ADVANCED_TRIGGERS = "advanced_triggers" } @@ -189,13 +189,13 @@ abstract class NavDestination(val isCompose: Boolean = false) { } @Serializable - data object ProMode : NavDestination(isCompose = true) { - override val id: String = ID_PRO_MODE + data object ExpertMode : NavDestination(isCompose = true) { + override val id: String = ID_EXPERT_MODE } @Serializable - data object ProModeSetup : NavDestination(isCompose = true) { - const val ID_PRO_MODE_SETUP = "pro_mode_setup_wizard" + data object ExpertModeSetup : NavDestination(isCompose = true) { + const val ID_PRO_MODE_SETUP = "expert_mode_setup_wizard" override val id: String = ID_PRO_MODE_SETUP } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/ViewModelHelper.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/ViewModelHelper.kt index cbb148c6fc..c6d68ec14c 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/ViewModelHelper.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/ViewModelHelper.kt @@ -8,134 +8,6 @@ import io.github.sds100.keymapper.system.SystemError import io.github.sds100.keymapper.system.permissions.Permission object ViewModelHelper { - suspend fun handleKeyMapperCrashedDialog( - resourceProvider: ResourceProvider, - dialogProvider: DialogProvider, - restartService: () -> Boolean, - ignoreCrashed: () -> Unit, - ) { - val dialog = DialogModel.Alert( - title = resourceProvider.getString(R.string.dialog_title_key_mapper_crashed), - message = resourceProvider.getText(R.string.dialog_message_key_mapper_crashed), - positiveButtonText = resourceProvider.getString( - R.string.dialog_button_read_dont_kill_my_app_yes, - ), - negativeButtonText = resourceProvider.getString( - R.string.dialog_button_read_dont_kill_my_app_no, - ), - neutralButtonText = resourceProvider.getString(R.string.pos_restart), - ) - - val response = dialogProvider.showDialog("app_crashed_prompt", dialog) ?: return - - when (response) { - DialogResponse.POSITIVE -> { - val popup = - DialogModel.OpenUrl(resourceProvider.getString(R.string.url_dont_kill_my_app)) - dialogProvider.showDialog("dont_kill_my_app", popup) - } - - DialogResponse.NEGATIVE -> ignoreCrashed() - - DialogResponse.NEUTRAL -> restartService() - } - } - - suspend fun showAccessibilityServiceExplanationDialog( - resourceProvider: ResourceProvider, - dialogProvider: DialogProvider, - ): DialogResponse { - val dialog = DialogModel.Alert( - title = resourceProvider.getString( - R.string.dialog_title_accessibility_service_explanation, - ), - message = resourceProvider.getString( - R.string.dialog_message_accessibility_service_explanation, - ), - positiveButtonText = resourceProvider.getString(R.string.enable), - negativeButtonText = resourceProvider.getString(R.string.neg_cancel), - ) - - val response = - dialogProvider.showDialog("accessibility_service_explanation", dialog) - ?: return DialogResponse.NEGATIVE - - return response - } - - suspend fun handleCantFindAccessibilitySettings( - resourceProvider: ResourceProvider, - dialogProvider: DialogProvider, - ) { - val dialog = DialogModel.Alert( - title = resourceProvider.getString( - R.string.dialog_title_cant_find_accessibility_settings_page, - ), - message = resourceProvider.getText( - R.string.dialog_message_cant_find_accessibility_settings_page, - ), - positiveButtonText = resourceProvider.getString( - R.string.pos_start_service_with_adb_guide, - ), - negativeButtonText = resourceProvider.getString(R.string.neg_cancel), - ) - - val response = - dialogProvider.showDialog("cant_find_accessibility_settings", dialog) ?: return - - if (response == DialogResponse.POSITIVE) { - val url = - resourceProvider.getString(R.string.url_cant_find_accessibility_settings_issue) - val openUrlPopup = DialogModel.OpenUrl(url) - - dialogProvider.showDialog("url_cant_find_accessibility_settings_issue", openUrlPopup) - } - } - - suspend fun handleAccessibilityServiceStoppedDialog( - resourceProvider: ResourceProvider, - dialogProvider: DialogProvider, - startService: () -> Boolean, - ) { - val explanationResponse = - showAccessibilityServiceExplanationDialog(resourceProvider, dialogProvider) - - if (explanationResponse != DialogResponse.POSITIVE) { - return - } - - if (!startService.invoke()) { - handleCantFindAccessibilitySettings(resourceProvider, dialogProvider) - } - } - - suspend fun handleAccessibilityServiceCrashedDialog( - resourceProvider: ResourceProvider, - dialogProvider: DialogProvider, - restartService: () -> Boolean, - ) { - val dialog = DialogModel.Alert( - title = resourceProvider.getString( - R.string.dialog_title_accessibility_service_explanation, - ), - message = resourceProvider.getString( - R.string.dialog_message_restart_accessibility_service, - ), - positiveButtonText = resourceProvider.getString(R.string.pos_restart), - negativeButtonText = resourceProvider.getString(R.string.neg_cancel), - ) - - val response = dialogProvider.showDialog("accessibility_service_explanation", dialog) - - if (response != DialogResponse.POSITIVE) { - return - } - - if (!restartService.invoke()) { - handleCantFindAccessibilitySettings(resourceProvider, dialogProvider) - } - } - suspend fun showFixErrorDialog( resourceProvider: ResourceProvider, dialogProvider: DialogProvider, diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt index 835108fffd..cf099840c3 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionButton.kt @@ -41,8 +41,8 @@ private fun Preview() { KeyMapperTheme { OptionButton( modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.title_pref_pro_mode), - text = stringResource(R.string.summary_pref_pro_mode), + title = stringResource(R.string.title_pref_expert_mode), + text = stringResource(R.string.summary_pref_expert_mode), onClick = {}, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt index f85de7a9d3..b01a3d0343 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/OptionPageButton.kt @@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.outlined.OfflineBolt import androidx.compose.material.icons.rounded.ChevronRight import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme @@ -23,8 +24,6 @@ import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R import io.github.sds100.keymapper.base.compose.KeyMapperTheme -import io.github.sds100.keymapper.base.utils.ui.compose.icons.KeyMapperIcons -import io.github.sds100.keymapper.base.utils.ui.compose.icons.ProModeIcon @Composable fun OptionPageButton( @@ -84,9 +83,9 @@ private fun Preview() { KeyMapperTheme { OptionPageButton( modifier = Modifier.fillMaxWidth(), - title = stringResource(R.string.title_pref_pro_mode), - text = stringResource(R.string.summary_pref_pro_mode), - icon = KeyMapperIcons.ProModeIcon, + title = stringResource(R.string.title_pref_expert_mode), + text = stringResource(R.string.summary_pref_expert_mode), + icon = Icons.Outlined.OfflineBolt, onClick = {}, ) } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt index f65b906060..ee835b2c0f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SetupRequirementRow.kt @@ -22,13 +22,13 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import io.github.sds100.keymapper.base.R -import io.github.sds100.keymapper.base.utils.ProModeStatus +import io.github.sds100.keymapper.base.utils.ExpertModeStatus @Composable -fun ProModeRequirementRow( +fun ExpertModeRequirementRow( modifier: Modifier = Modifier, isVisible: Boolean, - proModeStatus: ProModeStatus, + expertModeStatus: ExpertModeStatus, buttonColors: ButtonColors = ButtonDefaults.filledTonalButtonColors(), onClick: () -> Unit, ) { @@ -39,19 +39,21 @@ fun ProModeRequirementRow( ) { SetupRequirementRow( modifier = modifier, - text = stringResource(R.string.trigger_setup_pro_mode_title), + text = stringResource(R.string.trigger_setup_expert_mode_title), ) { - if (proModeStatus == ProModeStatus.UNSUPPORTED) { + if (expertModeStatus == ExpertModeStatus.UNSUPPORTED) { Text( - text = stringResource(R.string.trigger_setup_pro_mode_unsupported), + text = stringResource(R.string.trigger_setup_expert_mode_unsupported), color = MaterialTheme.colorScheme.error, style = MaterialTheme.typography.bodyMedium, ) } else { SetupRequirementButton( - enabledText = stringResource(R.string.trigger_setup_pro_mode_enable_button), - disabledText = stringResource(R.string.trigger_setup_pro_mode_running_button), - isEnabled = proModeStatus != ProModeStatus.ENABLED, + enabledText = stringResource(R.string.trigger_setup_expert_mode_enable_button), + disabledText = stringResource( + R.string.trigger_setup_expert_mode_running_button, + ), + isEnabled = expertModeStatus != ExpertModeStatus.ENABLED, colors = buttonColors, onClick = onClick, ) @@ -103,7 +105,9 @@ fun InputMethodRequirementRow( !isEnabled && enablingRequiresUserInput -> stringResource( R.string.trigger_setup_input_method_enable_button, ) + !isChosen -> stringResource(R.string.trigger_setup_input_method_choose_button) + else -> "" } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt similarity index 76% rename from base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt rename to base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt index f05dc715e0..8a362b7c4f 100644 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/SwitchText.kt +++ b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/SwitchText.kt @@ -1,4 +1,4 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons +package io.github.sds100.keymapper.base.utils.ui.compose import androidx.compose.foundation.clickable import androidx.compose.foundation.layout.Row @@ -16,18 +16,22 @@ import androidx.compose.ui.unit.dp @Composable fun SwitchText( - modifier: Modifier = Modifier, + modifier: Modifier = Modifier.Companion, text: String, isChecked: Boolean, isEnabled: Boolean = true, onCheckedChange: (Boolean) -> Unit, ) { - Surface(modifier = modifier, shape = MaterialTheme.shapes.medium, color = Color.Transparent) { + Surface( + modifier = modifier, + shape = MaterialTheme.shapes.medium, + color = Color.Companion.Transparent, + ) { Row( - modifier = Modifier + modifier = Modifier.Companion .clickable(enabled = isEnabled) { onCheckedChange(!isChecked) } .padding(8.dp), - verticalAlignment = Alignment.CenterVertically, + verticalAlignment = Alignment.Companion.CenterVertically, ) { Switch( enabled = isEnabled, @@ -37,7 +41,7 @@ fun SwitchText( ) Text( - modifier = Modifier.padding(horizontal = 12.dp), + modifier = Modifier.Companion.padding(horizontal = 12.dp), text = text, style = if (isEnabled) { @@ -50,7 +54,7 @@ fun SwitchText( ) }, maxLines = 2, - overflow = TextOverflow.Ellipsis, + overflow = TextOverflow.Companion.Ellipsis, ) } } diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt deleted file mode 100644 index 7e6435500b..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeDisabled.kt +++ /dev/null @@ -1,162 +0,0 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.PathFillType -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.StrokeCap -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.PathData -import androidx.compose.ui.graphics.vector.group -import androidx.compose.ui.graphics.vector.path -import androidx.compose.ui.unit.dp - -val KeyMapperIcons.ProModeIconDisabled: ImageVector - get() { - if (_ProModeDisabled != null) { - return _ProModeDisabled!! - } - _ProModeDisabled = ImageVector.Builder( - name = "ProModeDisabled", - defaultWidth = 32.dp, - defaultHeight = 32.dp, - viewportWidth = 32f, - viewportHeight = 32f, - ).apply { - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, -0f) - lineTo(32f, -0f) - lineTo(32f, 32f) - lineTo(-0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(4f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineTo(13f) - arcTo(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 8f, 11f) - horizontalLineTo(4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineTo(6f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(13f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(0.8f) - lineToRelative(1.2f, 4f) - horizontalLineToRelative(2f) - lineTo(17.76f, 16.85f) - curveTo(18.5f, 16.55f, 19f, 15.84f, 19f, 15f) - verticalLineToRelative(-2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineToRelative(-2f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(24f, 11f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, 2f) - verticalLineToRelative(6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, 2f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineToRelative(-6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-2f) - moveToRelative(0f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(6f) - horizontalLineToRelative(-2f) - close() - } - path( - fill = SolidColor(Color(0xFF808080)), - stroke = SolidColor(Color.Black), - strokeLineWidth = 2f, - strokeLineCap = StrokeCap.Round, - pathFillType = PathFillType.EvenOdd, - ) { - moveTo(26.664f, 5.353f) - lineTo(5.354f, 26.753f) - } - }.build() - - return _ProModeDisabled!! - } - -@Suppress("ObjectPropertyName") -private var _ProModeDisabled: ImageVector? = null diff --git a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt b/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt deleted file mode 100644 index ec3368f4da..0000000000 --- a/base/src/main/java/io/github/sds100/keymapper/base/utils/ui/compose/icons/ProModeIcon.kt +++ /dev/null @@ -1,150 +0,0 @@ -package io.github.sds100.keymapper.base.utils.ui.compose.icons - -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.SolidColor -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.graphics.vector.PathData -import androidx.compose.ui.graphics.vector.group -import androidx.compose.ui.graphics.vector.path -import androidx.compose.ui.unit.dp - -val KeyMapperIcons.ProModeIcon: ImageVector - get() { - if (_ProMode != null) { - return _ProMode!! - } - _ProMode = ImageVector.Builder( - name = "ProMode", - defaultWidth = 32.dp, - defaultHeight = 32.dp, - viewportWidth = 32f, - viewportHeight = 32f, - ).apply { - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, -0f) - lineTo(32f, -0f) - lineTo(32f, 32f) - lineTo(-0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(0f, 0f) - lineTo(32f, 0f) - lineTo(32f, 32f) - lineTo(0f, 32f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - group( - clipPathData = PathData { - moveTo(-0f, 32f) - lineTo(32f, 32f) - lineTo(32f, -0f) - lineTo(-0f, -0f) - close() - }, - ) { - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(4f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineTo(13f) - arcTo(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 8f, 11f) - horizontalLineTo(4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineTo(6f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(13f, 11f) - verticalLineToRelative(10f) - horizontalLineToRelative(2f) - verticalLineToRelative(-4f) - horizontalLineToRelative(0.8f) - lineToRelative(1.2f, 4f) - horizontalLineToRelative(2f) - lineTo(17.76f, 16.85f) - curveTo(18.5f, 16.55f, 19f, 15.84f, 19f, 15f) - verticalLineToRelative(-2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-4f) - moveToRelative(2f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(2f) - horizontalLineToRelative(-2f) - close() - } - path(fill = SolidColor(Color.Black)) { - moveToRelative(24f, 11f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, 2f) - verticalLineToRelative(6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, 2f) - horizontalLineToRelative(2f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, 2f, -2f) - verticalLineToRelative(-6f) - arcToRelative(2f, 2f, 0f, isMoreThanHalf = false, isPositiveArc = false, -2f, -2f) - horizontalLineToRelative(-2f) - moveToRelative(0f, 2f) - horizontalLineToRelative(2f) - verticalLineToRelative(6f) - horizontalLineToRelative(-2f) - close() - } - }.build() - - return _ProMode!! - } - -@Suppress("ObjectPropertyName") -private var _ProMode: ImageVector? = null diff --git a/base/src/main/res/drawable/offline_bolt_24px.xml b/base/src/main/res/drawable/offline_bolt_24px.xml new file mode 100644 index 0000000000..f81aae54c5 --- /dev/null +++ b/base/src/main/res/drawable/offline_bolt_24px.xml @@ -0,0 +1,10 @@ + + + diff --git a/base/src/main/res/drawable/pro_mode.xml b/base/src/main/res/drawable/pro_mode.xml deleted file mode 100644 index a0062fc0ed..0000000000 --- a/base/src/main/res/drawable/pro_mode.xml +++ /dev/null @@ -1,36 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/base/src/main/res/values-pt/strings.xml b/base/src/main/res/values-pt/strings.xml index 19202eb5bc..dd4af2f2a9 100644 --- a/base/src/main/res/values-pt/strings.xml +++ b/base/src/main/res/values-pt/strings.xml @@ -532,8 +532,8 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Escolha quais dispositivos acionam a troca automática de teclado Registros Isso pode adicionar latência aos seus mapas de teclas, portanto, ative-o somente se estiver tentando depurar o aplicativo ou se o desenvolvedor tiver solicitado. - Use o modo PRO - Detecção avançada de eventos importantes e muito mais + Use o modo PRO + Detecção avançada de eventos importantes e muito mais Luz Escuro Sistema @@ -1220,7 +1220,7 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Use o código de chave %d Use o código de digitalização %d Nenhum código de digitalização salvo - Use o modo PRO + Use o modo PRO Adicionar mais Remover @@ -1276,85 +1276,85 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. +%d restrições herdadas - Modo PRO - Importante! - Remapear botões no modo PRO é perigoso e pode fazer com que eles parem de funcionar se você os remapear incorretamente.\n\nSe você cometer um erro, pode ser necessário reiniciar o dispositivo à força segurando os botões de ligar e volume por 30 segundos — consulte o manual do seu dispositivo ou a internet para saber como fazer isso. - %d… - Eu entendo - Entendido - Configurar - Root detectado - Você pode pular o processo de configuração concedendo permissão de root ao Key Mapper. Isso permitirá que o Key Mapper inicie automaticamente o modo PRO na inicialização. - Iniciar modo PRO - Shizuku detectada - Você pode pular o processo de configuração dando permissão ao Key Mapper Shizuku. - Iniciar Shizuku - Solicitar permissão - Iniciar modo PRO - Configurar com o Key Mapper - Continuar - Continuar (Android 11+) - Opções - Habilitar modo PRO para todos os mapas principais - O Key Mapper usará o ADB Shell para remapeamento - Essas configurações ficarão indisponíveis até que você confirme o aviso. - O serviço do modo PRO está em execução - Parar - Iniciar automaticamente na inicialização - O Modo PRO será iniciado sempre que você ligar ou reiniciar seu dispositivo - Dica de emergência - Se o botão liga/desliga parar de funcionar, mantenha-o pressionado por 10 segundos e solte para desativar o Modo PRO. - Assistente de configuração - Etapa %d de %d - Use o assistente de configuração interativo - Interagir automaticamente com as configurações - Habilitar primeiro o serviço de acessibilidade - Assista ao tutorial - Iniciar serviço - Vá para as configurações - Iniciar serviço - Ativar serviço de acessibilidade - O Key Mapper usa um serviço para ajudar você a configurar o modo PRO. Ele também é útil para mapeamentos de teclas comuns. - Habilitar opções do desenvolvedor - O Key Mapper precisa usar o Android Debug Bridge para iniciar o modo PRO, e você precisa habilitar as opções do desenvolvedor para isso. - Conectar a uma rede WiFi - O Key Mapper precisa de uma rede Wi-Fi para habilitar o ADB. Você não precisa de conexão com a internet.\n\nSem rede Wi-Fi? Use um ponto de acesso do celular de outra pessoa. - Habilitar depuração sem fio - O Key Mapper usa depuração sem fio para iniciar seu serviço de remapeamento e entrada. - Emparelhar depuração sem fio - O Key Mapper precisa ser pareado com a depuração sem fio antes de poder iniciar seu serviço de remapeamento e entrada. - Iniciar serviço - O Key Mapper precisa se conectar ao Android Debug Bridge para iniciar o serviço do modo PRO. - Permitir notificações - O Key Mapper precisa de permissão para notificá-lo caso haja algum problema com o processo de configuração. - Dê permissão - Assistente de configuração - O modo PRO está em execução - Agora você pode remapear botões quando a tela estiver desligada e usar mais ações. - Terminar - Habilitar opções do desenvolvedor - Toque repetidamente no número da compilação - Emparelhamento automático - Procurando código de pareamento e porta… - Não é possível encontrar a porta e o código de emparelhamento - Toque no botão para parear com o código de pareamento e digite o código aqui - Falha ao iniciar o modo PRO - Toque para configurar novamente. Tente o pareamento ADB e reinicie o telefone se o problema persistir. - Modo PRO de inicialização automática - Usando root - Usando shizuku - Usando ADB via WiFi - Modo PRO iniciado - Divirta-se remapeando! ❤️ - Falha no emparelhamento - Mantenha o pop-up do código de emparelhamento na tela ao enviar o código de emparelhamento - Código de pareamento de entrada - O que posso fazer com o modo PRO? - 📲 Você pode remapear mais botões, como o botão de energia. + Modo PRO + Importante! + Remapear botões no modo PRO é perigoso e pode fazer com que eles parem de funcionar se você os remapear incorretamente.\n\nSe você cometer um erro, pode ser necessário reiniciar o dispositivo à força segurando os botões de ligar e volume por 30 segundos — consulte o manual do seu dispositivo ou a internet para saber como fazer isso. + %d… + Eu entendo + Entendido + Configurar + Root detectado + Você pode pular o processo de configuração concedendo permissão de root ao Key Mapper. Isso permitirá que o Key Mapper inicie automaticamente o modo PRO na inicialização. + Iniciar modo PRO + Shizuku detectada + Você pode pular o processo de configuração dando permissão ao Key Mapper Shizuku. + Iniciar Shizuku + Solicitar permissão + Iniciar modo PRO + Configurar com o Key Mapper + Continuar + Continuar (Android 11+) + Opções + Habilitar modo PRO para todos os mapas principais + O Key Mapper usará o ADB Shell para remapeamento + Essas configurações ficarão indisponíveis até que você confirme o aviso. + O serviço do modo PRO está em execução + Parar + Iniciar automaticamente na inicialização + O Modo PRO será iniciado sempre que você ligar ou reiniciar seu dispositivo + Dica de emergência + Se o botão liga/desliga parar de funcionar, mantenha-o pressionado por 10 segundos e solte para desativar o Modo PRO. + Assistente de configuração + Etapa %d de %d + Use o assistente de configuração interativo + Interagir automaticamente com as configurações + Habilitar primeiro o serviço de acessibilidade + Assista ao tutorial + Iniciar serviço + Vá para as configurações + Iniciar serviço + Ativar serviço de acessibilidade + O Key Mapper usa um serviço para ajudar você a configurar o modo PRO. Ele também é útil para mapeamentos de teclas comuns. + Habilitar opções do desenvolvedor + O Key Mapper precisa usar o Android Debug Bridge para iniciar o modo PRO, e você precisa habilitar as opções do desenvolvedor para isso. + Conectar a uma rede WiFi + O Key Mapper precisa de uma rede Wi-Fi para habilitar o ADB. Você não precisa de conexão com a internet.\n\nSem rede Wi-Fi? Use um ponto de acesso do celular de outra pessoa. + Habilitar depuração sem fio + O Key Mapper usa depuração sem fio para iniciar seu serviço de remapeamento e entrada. + Emparelhar depuração sem fio + O Key Mapper precisa ser pareado com a depuração sem fio antes de poder iniciar seu serviço de remapeamento e entrada. + Iniciar serviço + O Key Mapper precisa se conectar ao Android Debug Bridge para iniciar o serviço do modo PRO. + Permitir notificações + O Key Mapper precisa de permissão para notificá-lo caso haja algum problema com o processo de configuração. + Dê permissão + Assistente de configuração + O modo PRO está em execução + Agora você pode remapear botões quando a tela estiver desligada e usar mais ações. + Terminar + Habilitar opções do desenvolvedor + Toque repetidamente no número da compilação + Emparelhamento automático + Procurando código de pareamento e porta… + Não é possível encontrar a porta e o código de emparelhamento + Toque no botão para parear com o código de pareamento e digite o código aqui + Falha ao iniciar o modo PRO + Toque para configurar novamente. Tente o pareamento ADB e reinicie o telefone se o problema persistir. + Modo PRO de inicialização automática + Usando root + Usando shizuku + Usando ADB via WiFi + Modo PRO iniciado + Divirta-se remapeando! ❤️ + Falha no emparelhamento + Mantenha o pop-up do código de emparelhamento na tela ao enviar o código de emparelhamento + Código de pareamento de entrada + O que posso fazer com o modo PRO? + 📲 Você pode remapear mais botões, como o botão de energia. ⌨️ Use qualquer teclado com ações de código de tecla. ⭐️ As seguintes ações estão desbloqueadas: Wi-Fi, Bluetooth, dados móveis, NFC, modo avião, recolher barra de status e ligar/desligar a tela do dispositivo. - Mostrar informações do modo PRO - Descartar + Mostrar informações do modo PRO + Descartar O modo PRO parou inesperadamente Reiniciando automaticamente… Não reinicia automaticamente. Se você não estiver encerrando o serviço, informe o problema ao desenvolvedor. @@ -1413,10 +1413,10 @@ Deixe em branco se alguma rede Wi-Fi precisar ser correspondida. Habilitar Em execução Comprado - Modo PRO - Ativar gratuitamente - Em execução - Não disponível nesta versão do Android + Modo PRO + Ativar gratuitamente + Em execução + Não disponível nesta versão do Android Se o assistente do seu dispositivo for acionado por um botão de hardware, talvez seja possível remapeá-lo gratuitamente com o modo PRO. Tente escolher \"Outro\" na página de acionamento e siga as instruções. Este gatilho requer alguma configuração que varia de acordo com o dispositivo. Por favor, leia as instruções diff --git a/base/src/main/res/values-tr/strings.xml b/base/src/main/res/values-tr/strings.xml index 8175a0e00b..71fb475b1f 100644 --- a/base/src/main/res/values-tr/strings.xml +++ b/base/src/main/res/values-tr/strings.xml @@ -56,11 +56,15 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Tuş eşleme kimliği Rahatsız Etmeyin modunda düzgün çalışması için izin gerekli! Bu tetikleyici, telefon çalarken veya görüşme sırasında çalışmaz! - Android, telefonunuz çalarken veya bir görüşme sırasında erişilebilirlik servislerinin ses düğmesi basışlarını algılamasına izin vermez, ancak giriş yöntemi servisleri bunu algılayabilir. Bu nedenle, bu tetikleyicinin çalışmasını istiyorsanız Key Mapper klavyelerinden birini kullanmalısınız. + Android, telefonunuz çalarken veya bir telefon görüşmesi sırasında erişilebilirlik servislerinin ses düğmesine basılmasını algılamasına izin vermez, ancak giriş yöntemi servislerinin bunları algılamasına izin verir. Bu nedenle, bu tetikleyicinin çalışmasını istiyorsanız Key Mapper Giriş Yöntemi\'ni kullanmalısınız. Android sınırlamaları nedeniyle harekette çok fazla parmak var. Android sınırlamaları nedeniyle hareket süresi çok yüksek. - DPAD tetikleyicilerinin çalışması için bir Key Mapper klavyesi kullanmalısınız! - Tuş eşlemeleriniz rastgele duracak! + DPAD tetikleyicilerinin çalışması için Key Mapper Giriş Yöntemi\'ni kullanıyor olmalısınız! + PRO modu bu Android sürümünde desteklenmiyor + PRO modu başlatılmadı! + Tetikleyici cihaz bağlı değil! + Bu ekran kapalı tetikleyicisini taşıyın + Pil optimizasyonunu devre dışı bırakın. Tuş eşlemeleriniz duraklatıldı! Devam ettir Tuş eşlemelerinizin çalışması için erişilebilirlik servisinin açık olması gerekir! @@ -78,10 +82,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. %s uygulamasını aç Ekrana dokun (%d, %d) Ekrana dokun (%s) - %d parmakla %d/%d koordinatlarından %d/%d koordinatlarına %dms içinde kaydır - %d parmakla %d/%d koordinatlarından %d/%d koordinatlarına %dms içinde kaydır (%s) - %s, %d parmakla %d/%d koordinatlarında %dpx sıkıştırma mesafesiyle %dms içinde - %s, %d parmakla %d/%d koordinatlarında %dpx sıkıştırma mesafesiyle %dms içinde (%s) + %d parmakla (%d,%d) konumundan (%d,%d) konumuna %dms içinde kaydır + %d parmakla (%d,%d) konumundan (%d,%d) konumuna %dms içinde kaydır (%s) + %d parmakla (%d,%d) üzerinde %dpx sıkıştırma mesafesiyle %dms içinde %s + %d parmakla (%d,%d) üzerinde %dpx sıkıştırma mesafesiyle %dms içinde %s (%s) %s numarayı ara Ses çal: %s Bilinmeyen sesi oynat @@ -113,6 +117,8 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Girilecek metin Açılacak URL Aranacak telefon numarası + SMS gönderilecek telefon numarası + Gönderilecek mesaj Eylem Kategoriler Veri @@ -168,6 +174,8 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kaydedilmiş ses dosyanız yok! Key Mapper, Shizuku kullanarak kendine WRITE_SECURE_SETTINGS izni verdi Key Mapper, Root kullanarak kendine WRITE_SECURE_SETTINGS izni verdi + Mikrofon sessize alındı + Mikrofonun sesi açıldı Sıra tetikleyici zaman aşımı @@ -216,6 +224,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Yatay (270°) Dikey (herhangi) Yatay (herhangi) + Ekran yönü + Fiziksel yön + Fiziksel: Dikey + Fiziksel: Yatay + Fiziksel: Dikey (ters) + Fiziksel: Yatay (ters) Medya oynatan uygulama Medya oynatmayan uygulama Medya oynuyor @@ -226,10 +240,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. WiFi kapalı Bir WiFi ağına bağlı Bir WiFi ağından bağlantı kesildi - Android 10 ve daha yeni sürümlerde uygulamaların bilinen WiFi ağlarının listesini sorgulamasına izin verilmez, bu nedenle SSID’yi manuel olarak yazmanız gerekecek. - - Herhangi bir WiFi ağının eşleşmesi gerekiyorsa boş bırakın. - Herhangi biri + Herhangi bir WiFi ağıyla eşleşmesi gerekiyorsa boş bırakın. %s WiFi’ye bağlı %s WiFi’den bağlantı kesildi Herhangi bir WiFi’ye bağlı @@ -238,6 +249,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. %s seçildi Giriş yöntemi seçilmedi %s seçilmedi + Klavye gösteriliyor + Ekran klavyesi görünür + Klavye gösterilmiyor + Ekran klavyesi gizli Cihaz kilitli Cihaz kilidi açık Kilit ekranı gösteriliyor @@ -247,6 +262,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Telefon çalıyor Şarj oluyor Şarj olmuyor + Menteşe kapalı + Menteşe açık + Menteşe kapalı + Menteşe açık Dikey (0°) Yatay (90°) Dikey (180°) @@ -295,7 +314,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Her şeyi yedekle Dokunarak duraklat Dokunarak devam ettir - Kaydet + Paylaş Kısa mesajları aç/kapat Kopyala Temizle @@ -303,7 +322,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Eylem ekle Tetikleyiciyi kaydetmek için dokunun - Gelişmiş tetikleyiciler YENİ! Bitti Düzelt @@ -331,47 +349,23 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kaydedilmemiş değişiklikleriniz var. Bunları iptal ederseniz, düzenlemeleriniz kaybolacak. Lütfen Magisk gibi root yönetim uygulamanızda Key Mapper\'a root izni verin. WRITE_SECURE_SETTINGS izni ver - Bu izni vermek için bir PC/Mac gereklidir. Çevrimiçi kılavuzu okuyun. - Cihazınızda erişilebilirlik servisleri ayarları sayfası yok gibi görünüyor. Bunu nasıl düzelteceğinizi açıklayan çevrimiçi kılavuzu okumak için “kılavuz” seçeneğine dokunun. - Tuşlar, basılı tutulacakları sırayla yukarıdan aşağıya listelenmelidir. - Bir “sıra” tetikleyicisi, paralel tetikleyicilerden farklı olarak bir zaman aşımına sahiptir. Bu, ilk tuşa bastıktan sonra tetikleyicideki diğer tuşları girmeniz için belirli bir süreniz olacağı anlamına gelir. Tetikleyiciye eklediğiniz tüm tuşlar, zaman aşımı süresine ulaşılana kadar normal işlevlerini yerine getirmez. Bu zaman aşımını “Seçenekler” sekmesinden değiştirebilirsiniz. + Bu izni vermek için PRO modunu kullanmanız gerekecektir. + Cihazınızda bir erişilebilirlik servisleri ayarları sayfası görünmüyor. PRO modunu kurabilir veya \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\" ADB komutunu çalıştırabilirsiniz. + Tuşları listelendikleri sırayla basılı tutmalısınız. + Bu tetikleyiciyi girmek için bir zaman aşımı vardır. Bu zaman aşımını \"Seçenekler\" sekmesinden değiştirebilirsiniz. + Bu tetikleyici nasıl kullanılır + Sıralı tetikleyiciler Android, uygulamaların bağlı (eşleştirilmemiş) Bluetooth cihazlarının listesini almasına izin vermez. Uygulamalar yalnızca bu cihazların bağlandığını ve bağlantısının kesildiğini algılayabilir. Bu nedenle, Bluetooth cihazınız erişilebilirlik servisi başladığında zaten bağlıysa, uygulamanın bunu bilmesi için cihazı yeniden bağlamanız gerekecek. Otomatik yedekleme Konumu değiştir veya otomatik yedeklemeyi kapat? - PIN veya Desen gibi başka bir ekran kilidiniz varsa endişelenmenize gerek yok. Ancak yalnızca Parola ekran kilidi kullanıyorsanız, Key Mapper Temel Giriş Yöntemi’ni kullanırsanız telefonunuzun kilidini açamazsınız çünkü bu yöntemin bir arayüzü yoktur. Key Mapper’a WRITE_SECURE_SETTINGS izni vererek klavyeyi değiştirmek için bir bildirim gösterebilirsiniz. Bunu nasıl yapacağınızı öğrenmek için ekranın altındaki soru işaretine dokunun. + PIN veya Desen gibi başka bir ekran kilidi seçtiyseniz endişelenmenize gerek yok. Ancak Şifreli bir ekran kilidiniz varsa ve Key Mapper Giriş Yöntemi\'ni kullanıyorsanız, grafik arayüzü olmadığı için telefonunuzun kilidini açamazsınız. Key Mapper\'a WRITE_SECURE_SETTINGS izni vererek klavyeye geçiş yapmak için bir bildirim göstermesini sağlayabilirsiniz. Ekranın altındaki soru işaretine dokunarak bunu nasıl yapacağınıza dair bir kılavuz bulabilirsiniz. Eylemler için bir giriş yöntemi gerektiren bir yöntem seçin. Bunu daha sonra ana ekranın alt menüsündeki “Eylemler için klavye seç” seçeneğiyle değiştirebilirsiniz. - Caps Lock tuşunun hala büyük harf kilitlemesini engellemek için klavyenizde “Caps Lock’u kameraya” klavye düzenini seçmeniz gerekir. Bu ayarı cihaz ayarlarınızda -> Diller ve Giriş -> Fiziksel Klavye -> Klavyenize dokunun -> Klavye Düzenlerini Ayarla bölümünden bulabilirsiniz. Bu, Caps Lock tuşunu KEYCODE_CAMERA’ya yeniden eşleyerek Key Mapper’ın bunu doğru şekilde eşlemesini sağlar.\n\nBunu yaptıktan sonra Caps Lock tetikleyici tuşunu kaldırıp Caps Lock tuşunu tekrar kaydetmelisiniz. Adımları doğru yaptıysanız “Caps Lock” yerine “Kamera” yazmalıdır. Bağlı harici cihaz yok. - Key Mapper GUI Klavyesini Yükle - Bu şiddetle tavsiye edilir! Bu, Key Mapper ile kullanabileceğiniz uygun bir klavyedir. Key Mapper’a dahili olan (Temel Giriş Yöntemi) klavyede ekran klavyesi yoktur. Nereden yüklemek istediğinizi seçin. - Key Mapper Leanback Klavyesini Yükle - Bu şiddetle tavsiye edilir! Bu, Key Mapper ile kullanabileceğiniz Android TV için uygun bir klavyedir. Key Mapper’a dahili olan (Temel Giriş Yöntemi) klavyede ekran klavyesi yoktur. Nereden yüklemek istediğinizi seçin. - Key Mapper GUI Klavyesini Yükle - Nereden indirmek istediğinizi seçin. - Key Mapper Leanback Klavyesini Yükle - Nereden indirmek istediğinizi seçin. - Bu eylem için ek kurulum gerekiyor - Bu eylemi kullanmak için cihazınızı ayarlamanın 3 yolu var. Her birinin avantajları ve dezavantajları şunlardır: - - \n\n1. Shizuku’yu indirin (önerilen). Şu anda kullandığınız ekran klavyesini değiştirmeniz gerekmez, ancak cihazınızı her yeniden başlattığınızda bir dakikalık kurulum gerektirir. - - \n\n2. Key Mapper GUI Klavyesini indirin. Bu, Key Mapper ile kullanabileceğiniz bir ekran klavyesidir, ancak şu anda kullandığınız klavyeyi (örneğin Gboard) kullanamazsınız. - - \n\n3. Hiçbir şey yapmayın ve dahili Key Mapper klavyesini kullanın. Bu önerilmez çünkü Key Mapper’ı kullandığınızda hiçbir ekran klavyeniz olmaz! Hiçbir avantajı yoktur. - Bu eylem için ek kurulum gerekiyor - Bu eylemi kullanmak için cihazınızı ayarlamanın 3 yolu var. Her birinin avantajları ve dezavantajları şunlardır: - - \n\n1. Shizuku’yu indirin (önerilen). Şu anda kullandığınız ekran klavyesini değiştirmeniz gerekmez, ancak cihazınızı her yeniden başlattığınızda bir dakikalık kurulum gerektirir. - - \n\n2. Key Mapper Leanback Klavyesini indirin. Bu, Key Mapper ile kullanabileceğiniz Android TV için optimize edilmiş bir ekran klavyesidir, ancak şu anda kullandığınız klavyeyi (örneğin Gboard) kullanamazsınız. - - \n\n3. Hiçbir şey yapmayın ve dahili Key Mapper klavyesini kullanın. Bu önerilmez çünkü Key Mapper’ı kullandığınızda hiçbir ekran klavyeniz olmaz! Hiçbir avantajı yoktur. Pil optimizasyonunu devre dışı bırak HEPSİNİ okumanız ZORUNLU, aksi takdirde ileride sinir bozucu sorunlar yaşarsınız!\n\n“Kısmen düzelt” seçeneğine dokunmak, Android’in uygulamayı arka planda durdurmasını belki engelleyebilir.\n\nBu YETERLİ DEĞİL. MIUI veya Samsung Experience gibi OEM arayüzünüzde başka uygulama kapatma özellikleri olabilir, bu nedenle dontkillmyapp.com’daki çevrimiçi kılavuzu takip ederek Key Mapper için bunları da kapatmalısınız. Erişilebilirlik servisini kapatıp açarak yeniden başlatın. - Bu tetikleyiciyi kullanmak, cihazınızın ayarlarındaki ekran sabitleme ayarını kullandıktan sonra cihazınızın kilidini açtığınızda siyah bir ekran oluşmasına neden olabilir. Bu, bir yeniden başlatma ile düzeltilebilir. Bu durum tüm cihazlarda olmaz, bu yüzden dikkatli olun ve sorun yaşarsanız ayarı kapatın! Key Mapper kesintiye uğradı - Key Mapper arka planda çalışmaya çalıştı ancak sistem tarafından durduruldu.\nBu, pil veya bellek optimizasyonu açık olduğunda olabilir.\n\nBunu düzeltmek için çevrimiçi bir kılavuzu takip edebilirsiniz. İşiniz bittiğinde servisi de yeniden başlatmalısınız. + Key Mapper arka planda çalışmayı denedi ancak sistem tarafından durduruldu.\nBu durum, pil veya bellek optimizasyonu açık olduğunda meydana gelebilir.\n\nBunu düzeltmek için çevrimiçi bir kılavuzu takip etmeyi deneyebilirsiniz. İşiniz bittiğinde servisi de yeniden başlatmalısınız. Devam et Yoksay Hata raporu oluşturulamadı @@ -380,14 +374,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Key Mapper için dosya seçmenize izin veren bir dosya uygulamanız yüklü değil. Lütfen bir dosya yöneticisi yükleyin. Erişilebilirlik servisi etkinleştirilmeli @string/accessibility_service_explanation + Kısıtlanmış Ayarlara izin vermeniz gerekebilir. + Talimatları okumak için dokunun. Rahatsız Etmeyin erişimini ver Cihazınızın hangi uygulamaların Rahatsız Etmeyin durumunu değiştirebileceğini yönetebileceğiniz ayarlar sayfasına yönlendirileceksiniz. Bu bazı cihazlarda mevcut değildir, bu yüzden listede Key Mapper’ı görmüyorsanız “tekrar gösterme” seçeneğine dokunun. - Bilmenizde fayda var! - Bir tetikleyici tuşunun yanında bu sembolü (⌨) görüyorsanız, algılanması için bir Key Mapper klavyesi KULLANMALISINIZ. Bu, Android’deki bir kısıtlamadır ve yalnızca bazı düğmeler için gereklidir. - Önemli! - Key Mapper GUI Klavyesini, bu Key Mapper sürümüyle uyumlu olacak şekilde güncellemelisiniz. Güncelleme yapana kadar bazı tuş eşlemeleri çalışmayabilir! - Şimdi güncelle - Yoksay Şuna göre sırala Öncelikleri ayarlamak için tutamaçları sürükleyin. En üstteki öğe en önemlisidir. Ayrıca herhangi bir öğeye dokunarak sıralama sırasını tersine çevirebilirsiniz. Örnek: Tuş eşlemelerini öncelikle Eylemlerine göre artan sırayla ve ikincil olarak Tetikleyicilerine göre azalan sırayla sıralamak için Eylemleri birinci sıraya, Tetikleyicileri ikinci sıraya taşıyın. @@ -395,9 +385,14 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Örnek göster Bildirimleri aç Bazı eylemler ve seçenekler bu izni gerektirir. Ayrıca uygulama hakkında önemli haberler olduğunda bildirim alabilirsiniz. + Bu ekran kapalı tetikleyicisini taşıyın + Kesinti için üzgünüz. Eski ekran kapalı yeniden eşleme seçeneğinin yerini alan PRO modu adında *ücretsiz* bir özellik getirdik. Bu özellik çok daha güvenilir ve güç düğmesini yeniden eşleme özelliğini de açıyor.\n\nÇalışma şekli nedeniyle bu tetikleyiciyi PRO modu ile yeniden kaydetmeniz gerekecek. + Devam et + READ_LOGS iznini verin + Bunun çalışması için rootlu olmanız veya PRO modunu etkinleştirmiş olmanız gerekir. Sistem bu izni verdiğinde uygulamayı kapatacaktır. Uygulamayı tekrar kendiniz açmanız ve logcat\'i tekrar paylaşmayı denemeniz gerekecek. Bitti - Kılavuz - Kılavuz + Durdur + PRO modunu kullan Değiştir Kısmen düzelt Tamam @@ -408,6 +403,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kaydet Anladım + Devam et Kapat İptal Tekrar gösterme @@ -418,17 +414,14 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Ayarlar Belgeler Değişiklik günlüğü - Shizuku - Key Mapper GUI Klavyesi - Key Mapper Leanback Klavyesi - Hiçbir şey yapma Düzelt Tuş eşlemelerini duraklat/devam ettir Klavye gizli uyarısı - Key Mapper klavyesini aç/kapat + Key Mapper Giriş Yöntemini Değiştir Yeni özellikler + Özel bildirimler Çalışıyor Key Mapper’ı açmak için dokunun. Duraklat @@ -445,9 +438,11 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Servisi durdur Klavye gizli! Klavyeyi tekrar göstermeye başlamak için ‘klavyeyi göster’e dokunun. - Key Mapper klavyesini aç/kapat - Key Mapper klavyesine ve klavyenizden geçiş yapmak için ‘aç/kapat’a dokunun. + Key Mapper Giriş Yöntemini Değiştir + Key Mapper Giriş Yöntemi\'ne geçiş yapmak için \'değiştir\'e dokunun. Aç/Kapat + Ekran kapalı tuş eşlemeleriniz dikkat gerektiriyor! + Kesinti için üzgünüz ancak ekran kapalı tuş eşlemelerinizi yeniden kaydetmeniz gerekecek. Varsayılan uzun basış gecikmesi @@ -471,19 +466,19 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Otomatik yedeklemeyi aç Tuş eşlemelerinizi periyodik olarak yedekleyin Bir cihaz (örneğin klavye) bağlandığında/bağlantısı kesildiğinde ekran klavyesini otomatik olarak değiştir - Seçilen bir cihaz bağlandığında son kullanılan Key Mapper klavyesi otomatik olarak seçilir. Cihazın bağlantısı kesildiğinde normal klavyeniz otomatik olarak seçilir. + Seçilen bir cihaz bağlandığında Key Mapper Giriş Yöntemi otomatik olarak seçilecektir. Cihazın bağlantısı kesildiğinde ise normal klavyeniz otomatik olarak seçilecektir. Metin girmeye başladığınızda ekran klavyesini otomatik olarak değiştir - Klavyeyi açmaya çalıştığınızda son kullanılan Key Mapper dışı klavye otomatik olarak seçilir. Klavyeyi kullanmayı bıraktığınızda Key Mapper klavyeniz otomatik olarak seçilir. + Klavyeyi açmaya çalıştığınızda son kullanılan normal ekran klavyesi otomatik olarak seçilecektir. Klavyeyi kullanmayı bıraktığınızda ise Key Mapper Giriş Yöntemi otomatik olarak seçilecektir. Ekran üstü mesaj Klavyeyi otomatik olarak değiştirirken göster Root izni iste Cihazınız rootlu ise bu, Magisk\'ten veya root uygulamanızdan root izni penceresini gösterecektir. Tema seç Açık ve koyu temalar mevcut - Bildirime dokunduğunuzda Key Mapper klavyesi ile varsayılan klavyeniz arasında geçiş yapın. - Key Mapper klavyesini aç/kapat bildirimi + Bildirime dokunduğunuzda Key Mapper Giriş Yöntemi ile normal klavyeniz arasında geçiş yapın. + Key Mapper Giriş Yöntemini değiştir bildirimini göster Tuş eşlemelerini açarken/kapatırken klavyeyi otomatik olarak değiştir - Tuş eşlemelerinizi devam ettirdiğinizde Key Mapper klavyesini, duraklattığınızda ise varsayılan klavyenizi otomatik olarak seçin. + Tuş eşlemelerinizi devam ettirdiğinizde Key Mapper Giriş Yöntemi\'ni otomatik olarak seçin ve duraklattığınızda varsayılan klavyenizi seçin. Ana ekran uyarılarını gizle Ana ekranın üstündeki uyarıları gizle Cihaz kimliklerini göster @@ -525,17 +520,14 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bu seçenekler yalnızca rootlu cihazlarda çalışır! Rootun ne olduğunu veya cihazınızın rootlu olup olmadığını bilmiyorsanız, bunlar çalışmazsa lütfen kötü bir inceleme bırakmayın. :) WRITE_SECURE_SETTINGS izni gerektirir Bu seçenekler yalnızca Key Mapper WRITE_SECURE_SETTINGS iznine sahipse etkinleşir. İzni nasıl vereceğinizi öğrenmek için aşağıdaki düğmeye tıklayın. - Shizuku desteği - Shizuku, Key Mapper’ın yalnızca sistem uygulamalarının yapabileceği şeyleri yapmasını sağlayan bir uygulamadır. Örneğin, Key Mapper klavyesini kullanmanız gerekmez. Bunu nasıl kuracağınızı öğrenmek için dokunun. - Shizuku\'yu kurmak için bu adımları izleyin Klavyeyi otomatik olarak değiştir Gerektiğinde değiştir, sonra geri dön Cihazları seç Hangi cihazların otomatik klavye değiştirmeyi tetikleyeceğini seçin Günlük kaydı Bu, tuş eşlemelerinize gecikme ekleyebilir, bu yüzden yalnızca uygulamayı hata ayıklamaya çalışıyorsanız veya geliştirici tarafından istenmişse açın. - PRO modunu kullan - Gelişmiş tuş olayı algılama ve daha fazlası + PRO modunu kullan + Gelişmiş tuş olayı algılama ve daha fazlası Açık Koyu Sistem @@ -555,7 +547,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Basılı tut Tekrar basılana kadar basılı tut Yeniden eşleme yapma - Bu tuş eşlemesini diğer uygulamaların tetiklemesine izin ver + Diğer uygulamaların bu tuş eşlemesini intent\'ler veya kısayollarla kontrol etmesine izin ver Tuş eşleme kimliğini kopyala @@ -584,7 +576,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Çalışıyor Servis Devre Dışı Key Mapper erişilebilirlik servisi devre dışı - Key Mapper klavyesini aç/kapat + Key Mapper Giriş Yöntemini Değiştir Ctrl @@ -606,7 +598,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Scroll Lock - Bu eylemin çalışması için Key Mapper klavyelerinden birini kullanıyor olmanız gerekiyor! + Bu eylemin çalışması için Key Mapper Giriş Yöntemi\'ni kullanıyor olmalısınız! %s paket adına sahip uygulama yüklü değil! %s uygulaması devre dışı! Key Mapper\'a sistem ayarlarını değiştirme izni vermeniz gerekiyor. @@ -621,7 +613,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Cihazınız Bluetooth\'u desteklemiyor. Cihazınız cihaz politikası uygulamasını desteklemiyor. Cihazınızda kamera flaşı bulunmuyor. - Cihazınızda telefon özellikleri bulunmuyor. + Cihazınız telefon özelliklerini kullanamıyor. SIM kart takılı mı? Klavye ayarları sayfası bulunamadı! Key Mapper\'ın cihaz yöneticisi olması gerekiyor! Key Mapper\'ın bu kısayolu kullanma izni yok @@ -632,6 +624,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Rahatsız Etmeyin erişim izni ayarları bulunamadı! Key Mapper\'ın WRITE_SECURE_SETTINGS iznine ihtiyacı var. Bu telefon aramasını başlatabilecek bir uygulama yok + Bu SMS\'i gönderebilecek bir uygulama yok Kamera kullanımda! Kamera bağlantısı kesildi! Kamera devre dışı! @@ -643,11 +636,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Erişilebilirlik servisinin etkinleştirilmesi gerekiyor! Erişilebilirlik servisinin yeniden başlatılması gerekiyor! Başlatıcınız kısayolları desteklemiyor. - Bir Key Mapper klavyesinin etkinleştirilmesi gerekiyor! + Key Mapper Giriş Yöntemi\'nin etkinleştirilmesi gerekiyor! %s giriş yöntemi bulunamadı Giriş yöntemi seçici gösterilemiyor! Erişilebilirlik düğümü bulunamadı! %s genel eylemi gerçekleştirilemedi! + Bu eylemin ayarlanması gerekiyor Pil optimizasyon ayarları bulunamadı! Varsa, manuel olarak açın. Ekstra (%s) bulunamadı! Aynı kısıtlamaya iki kez sahip olamazsınız! @@ -664,10 +658,21 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bildirim erişim izni reddedildi! Geçersiz! Telefon araması başlatma izni reddedildi! + SMS mesajı gönderme izni reddedildi! + SMS gönderilemedi. Numara doğru mu? + SMS gönderilemiyor. Uçak modunu kapatın. + SMS gönderilemiyor. Hücresel servis yok. + SMS gönderme sınırı aşıldı. Daha sonra tekrar deneyin. + Ağ SMS\'i reddetti. + SMS gönderilemiyor. Cihazın belleği dolu. + Geçersiz SMS mesaj formatı. + SMS gönderilemiyor. Ağ hatası. + Acil arama sırasında SMS gönderilemez. + SMS gönderilemiyor. SIM kart algılanmadı. Bu yedeği kullanmak için Key Mapper\'ı en son sürüme güncellemeniz gerekiyor. Sesli asistan yüklü değil! Yetersiz izinler - Yalnızca Key Mapper klavyeleri yüklü! + Yüklü ekran klavyeniz yok! Medya oynatan bir uygulama yok! Kaynak dosya bulunamadı! %s Hedef dosya bulunamadı! %s @@ -675,6 +680,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Sistem ayarı %s değiştirilemedi! %s etkinleştirilmeli! Giriş yöntemi değiştirilemedi! + Giriş yöntemi etkinleştirilemedi! Cihazınızda kamera uygulaması yok! Cihazınızda asistan yok! Cihazınızda ayarlar uygulaması yok! @@ -696,6 +702,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Telefon aramalarını yanıtlama ve sonlandırma izni reddedildi! Eşleştirilmiş Bluetooth cihazlarını görme izni reddedildi! Bildirim gösterme izni reddedildi! + Günlükleri okuma izni reddedildi! 2 veya daha fazla olmalı! %d veya daha az olmalı! 0\'dan büyük olmalı! @@ -704,7 +711,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. 0\'dan büyük olmalı! %d veya daha az olmalı! UI öğesi bulunamadı! + Komut %1$d saniye sonra zaman aşımına uğradı PRO Modunun başlatılması gerekiyor + Hız sınırına ulaşıldı. Saniyede yalnızca bir kez gönderebilirsiniz. WiFi\'yi aç/kapat @@ -718,6 +727,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Sesi kapat Sesi aç/kapat Sesi aç + Mikrofonu sessize al + Mikrofonun sesini aç + Mikrofon sesini aç/kapat Ses diyaloğunu göster Akışı artır %s akışını artır @@ -743,6 +755,9 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Mobil veriyi aç/kapat Mobil veriyi aç Mobil veriyi kapat + Hotspot\'u aç/kapat + Hotspot\'u aç + Hotspot\'u kapat Otomatik parlaklığı aç/kapat Otomatik parlaklığı kapat Otomatik parlaklığı aç @@ -866,9 +881,32 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Telefon araması başlat Telefon aramasını cevapla Telefon aramasını sonlandır + SMS gönder + SMS gönder: \"%s\"\" - %s + SMS oluştur + SMS oluştur: \"%s\" - %s + Ayarı ayarla: %1$s = %2$s + Sistem + Güvenli + Genel + Ayarı değiştir Ses çal En son bildirimi kaldır Tüm bildirimleri kaldır + Bildirim oluştur + Bildirimi göster: %1$s + Bildirim başlığı + Bildirim başlığını girin + Başlık boş olamaz + Bildirim içeriği + Bildirim içeriğini girin + İçerik boş olamaz + Bildirimi otomatik kapat + Şu süre sonra otomatik kapat + %d saniye + Test et + Test ediliyor… + Bildirim başarıyla gösterildi Cihaz kontrol ekranı HTTP isteği HTTP Yöntemi @@ -880,6 +918,31 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. İstek gövdesi (opsiyonel) Yetkilendirme başlığı (opsiyonel) Gerekirse \'Bearer\' ön ekini kullanın + Shell komutu + Shell komutu eylemi + Betik + Betik boş olamaz! + \'adb shell\' yazmayın! + Root olarak çalıştır + Standart + Root + ADB + Çalıştırma Modu + ADB modu akış çıktısını desteklemiyor + PRO Modunu Kur + PRO Modunu Kur (Desteklenmiyor) + Yapılandırma + Çıktı + Henüz çıktı yok. Komutu çalıştırmak için Test\'e tıklayın. + Test et + Çıktı + Çalıştırılıyor… + Başarılı + Başarısız + Çıkış kodu: %1$d + Root ile çalıştır: %s + ADB ile çalıştır: %s + Çalıştır: %s Uygulama öğesiyle etkileşime geç Key Mapper, menüler, sekmeler, düğmeler ve onay kutuları gibi uygulama öğelerini algılayabilir ve bunlarla etkileşime girebilir. Key Mapper\'ın ne yapmak istediğinizi bilmesi için, uygulama öğesiyle etkileşiminizi kaydetmeniz gerekmektedir. Kaydetmeye başla @@ -916,6 +979,20 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Kaynak kimliğini görüntüle Özgün kimlik Etkileşim türü + Uygulamayı durmaya zorla + Uygulamayı son uygulamalardan kapat ve temizle + Ayarı değiştir + Anahtar + Değer + Ayar anahtarı boş olamaz + Ayar değeri boş olamaz + Not: Yalnızca ayar değerlerini değiştirmek, sistemin değişikliği işlemesi için yeterli olmayabilir. Bazı ayarların etkili olması için ek eylemler veya yayınlar gerekir. + Test et + Ayar başarıyla değiştirildi + İzin ver + Ayar seçin + Ayar bulunamadı + Mevcut ayarı seçin Navigasyon @@ -932,6 +1009,20 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bildirimler Özel + + Uygulamalar + Medya + Bluetooth + Ekran + El feneri + WiFi + Klavye + Kilit + Telefon + Güç + Cihaz + Zaman + Boolean Boolean dizisi @@ -972,15 +1063,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Java programlama dilinde geçerli Kısa sayıların virgülle ayrılmış bir listesi. Örn. 3242,12354 Intent bayrakları bit bayrakları olarak saklanır. Bu bayraklar Intent\'in nasıl işleneceğini değiştirir. Bir Etkinlik Intent\'i için bu alan boş bırakılırsa, Key Mapper varsayılan olarak FLAG_ACTIVITY_NEW_TASK kullanır. Daha fazla bilgi için Android geliştirici belgelerini görmek üzere \'dokümanlar\'a dokunun. - - Öğreticiyi geç - İlk anahtar haritanı oluştur! - Bir anahtar haritası, bir düğmeye basıldığında cihazınızın ne yapacağını belirten bir kuraldır. - Bir eylem seçin - Bir eylem, tetikleyiciye bastığınızda olması gereken şeydir. - Bir kısıtlama seç (isteğe bağlı) - Anahtar haritasının yalnızca belirli durumlarda çalışmasını istiyorsanız, örneğin, bir uygulama açıkken. - GitHub Web Sitesi @@ -1001,24 +1083,42 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Çevirmen (Çekçe) Çevirmen (İspanyolca) + + Key Mapper\'ı Destekleyin ❤️ + Deneyiminizi yükseltmek için eklentileri seçin + Desteğiniz Key Mapper\'ı ayakta tutuyor! + "Kayan düğme özelliği ezber bozan bir yenilik" + Google Play yorumcusu + Kayan düğmeler ezber bozan bir yenilik! + Kayan düğmeler özelliğini keyifle kullanıyorum. + Kayan düğmeler harika bir eklenti. + Telefon deneyimimin vazgeçilmez bir parçası. + Bir kez öde, sonsuza dek kullan + Videoyu izle + Şimdi satın al (%s) + Daha fazla bilgi al + Ekran üstü kayan düğmeler + Yeterli düğmeniz yok mu? Herhangi bir uygulamada, oyunda veya kilit ekranınızda anında kısayollar ve makrolar oluşturun! + Oyunlarda kullan + Kilit ekranında kullan + Yan tuş ve Asistan tetikleyicisi + İşe yaramaz yan tuş mu? Asistan düğmesini veya cihazınızın yan tuş kısayolunu istediğiniz her şeye yeniden atayın! + Ekran kapalı + Bixby + Gemini Key Mapper: Yan Tuş Herhangi bir asistan Yan tuş/güç düğmesi Sesli asistan - Gelişmiş tetikleyiciler - Geliştirici, reklamların sürdürülebilir veya kullanıcı dostu bir gelir modeli olduğuna inanmıyor, bu nedenle bu ücretli tetikleyiciler geliştirmeyi desteklemeye yardımcı oluyor ❤️. Ayrıca öncelikli destek de alacaksınız. - Yan tuş ve Asistan tetikleyicisi - Yan tuşunuzu, güç düğmenizi veya cihaz asistanınızı yeniden eşleyebileceğinizi biliyor muydunuz? Asistanı veya güç menüsünü başlatmak yerine, cihazınız seçtiğiniz bir eylemi gerçekleştirebilir. Ekran kapalıyken bile çalışır! Yan tuş tetikleyici özelliğini satın almanız gerekiyor. - Daha fazla bilgi Kayan düğmeleri satın almanız gerekiyor. Düğme silindi. Kayan düğme Satın alma doğrulanamıyor. İnternet bağlantınız var mı? Kilidi aç (%s) - Kullan + Kullan Yükleniyor… Satın alındı! Fiyatı tekrar almayı dene @@ -1033,25 +1133,12 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bir şeyler ters gitti 😕 Tekrar dene Geliştiriciyle iletişime geç - Yan tuş tetikleyici özelliğini satın almanız gerekiyor! Tuş eşlemesine dokunun ve ardından \'Gelişmiş tetikleyiciler\'e tıklayarak satın alın. - Kayan düğmeler özelliğini satın almanız gerekiyor! Tuş eşlemesine dokunun ve ardından \'Gelişmiş tetikleyiciler\'e tıklayarak satın alın. - Uygulamayı desteklediğiniz için teşekkürler ❤️! + Yan tuş tetikleyici özelliğini satın almalısınız! Tuş eşlemesine dokunun ve ardından mağazaya tıklayarak satın alın. + Kayan düğmeler özelliğini satın almalısınız! Tuş eşlemesine dokunun ve ardından mağazaya tıklayarak satın alın. + Uygulamayı desteklediğiniz için teşekkürler! Satın alımınız başarılı oldu. Key Mapper\'ın ücretli bir kullanıcısı olarak uygulamayı kullanmanıza yardımcı olmak için öncelikli destek alacaksınız. Artık mağazada geliştiriciyle iletişime geçmek için bir düğme bulunmaktadır. - Gelişmiş tetikleyiciler ücretli bir özelliktir ancak siz FOSS yapısını indirdiniz ve bu yapı Google Play faturalandırmasını içermiyor. Bu özelliğe erişmek için lütfen Key Mapper\'ı Google Play\'den indirin. + Gelişmiş tetikleyiciler ücretli bir özelliktir ancak siz, bu kapalı kaynak modülünü veya Google Play faturalandırmasını içermeyen Key Mapper\'ın FOSS sürümünü indirdiniz. Bu özelliğe erişmek için lütfen Key Mapper\'ı Google Play\'den indirin. Play sürümünü indir - - DPAD düğmelerini yeniden eşlemek ister misiniz? - Aşağıdaki adımları izleyerek Key Mapper GUI Klavyesini ayarlamanız gerekiyor. - 1. Klavye uygulamasını yükle - Yükle - Yüklendi - 2. Klavyeyi etkinleştir - Etkinleştir - Etkin - 3. Klavyeyi kullan - Klavyeyi değiştir - Klavye seçildi - Kurulum tamamlandı! \'Bitti\'ye dokunun ve DPAD tetikleyiciniz çalışmalı. Düğme algılanmadı mı? Erişilebilirlik servisi yerine tetikleyicinizi kaydetmek için Key Mapper GUI Klavye uygulamasını deneyebilirsiniz. @@ -1078,12 +1165,27 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. İptal Bitti Düğmenin metni olmalı! + Bildirim panelinin üzerinde göster + Klavyenin üzerinde göster Kayan düğmeler Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. Kayan düğme %s (%s) Silinen kayan düğme - Kısıtlamalar - Bu düğmenin yalnızca bazı uygulamalarda ekranda olmasını ister misiniz? Bu tuş eşlemesi için “Kısıtlamalar” sekmesinde bir “Ön plandaki uygulama” kısıtlaması ekleyin. + Daha iyi Caps Lock uyumluluğu + PRO modu, Caps Lock yeniden eşlemesi için daha iyi uyumluluk sağlar. \'PRO modunu kullan\' seçeneğine dokunun ve tekrar kaydedin. + PRO modunu kullan + Ekran kapalıyken yeniden eşleme? + PRO modu ile artık herhangi bir tetikleyici ekran kapalıyken ücretsiz olarak çalışabilir! Tekrar kaydetmeniz gerekecek. + PRO modunu kullan + Uygulama sabitleme uyarısı + Geri düğmesini tetikleyici olarak kullanmak uygulama sabitleme ile çakışabilir ve kilit açıldığında siyah ekrana neden olabilir. Yeniden başlatma sorunu çözecektir. + Klavye simgesi + ⌨ simgesi, bir Android kısıtlaması nedeniyle bu tetikleyicinin çalışması için Key Mapper giriş yöntemini kullanmanız gerektiği anlamına gelir. + Zil modu eylemleri + Rahatsız Etmeyin ayarlarıyla çakışmaları önlemek için zil modu eylemleri için PRO modunu kullanmayı düşünün. + PRO modunu kullan + Belirli uygulamalarla sınırla? + Kısıtlamalar sekmesinde kısıtlamalar ekleyin. Bir düzen seçin Arka Yardım @@ -1096,8 +1198,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Düğmeyi yapılandır Düzeni düzenle Android 11 veya daha yeni bir sürüm gerektiriyor. - Yeterince düğmeniz yok mu? Artık kendi düğmelerinizi yapabilirsiniz! - Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. Tetikleyici Eylemler @@ -1108,7 +1208,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Yeni tuş eşlemesi Yeni düzen Bir tuş eşlemesini yapılandırmak için dokunun.\nDaha fazla seçenek için uzun basın. - Bir tuş eşlemesi oluşturun! + Bir tuş eşlemesi oluşturun!\n\nTuş eşlemesi, bir düğmeye basıldığında cihazınıza ne yapacağını söyleyen bir kuraldır. Yeterince düğmeniz yok mu? Kendi düğmelerinizi yapın! Kayan düğmeler istediğiniz uygulamaların üzerinde görünür. Gerçek düğmeler gibi çalışırlar ve onları istediğiniz gibi yerleştirebilir, stil verebilir ve eşleyebilirsiniz. @@ -1131,7 +1231,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Evet, sil İptal Kayan düzenleri gizle - Kayan düğmeleri, bir tetikleyici oluştururken Gelişmiş Tetikleyiciler düğmesinde bulabilirsiniz. + Tetikleyici oluştururken Mağaza düğmesinde kayan düğmeleri bulabilirsiniz. Kayan düğmeler Menü Sırala @@ -1213,7 +1313,6 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Parmak izi okuyucuda aşağı kaydır Parmak izi okuyucuda sola kaydır Parmak izi okuyucuda sağa kaydır - Gelişmiş tetikleyiciler Tuş kodu %d Tarama kodu %d Kodu tara @@ -1222,7 +1321,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Tuş kodu %d kullan Tarama kodu %d kullan Kaydedilmiş tarama kodu yok - PRO modu ile kaydet + PRO modu ile kaydet Daha fazla ekle Kaldır @@ -1250,7 +1349,11 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Android 13 veya daha yeni bir sürüm gerektiriyor. Bu cihaz parlaklık değişikliğine izin vermiyor. Parlaklık değişikliği + Ses akışı + Varsayılan (sistem kontrollü) + Seçenekler Desteklenmiyor + Desteklenmiyor Tuş eşlemelerinin yalnızca belirli durumlarda çalışmasını istiyorsanız kısıtlamalar ekleyin. Son kullanılan kısıtlamalar @@ -1278,86 +1381,91 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. +%d devralınan kısıtlamalar - PRO modu - Önemli! - PRO modu ile tuşları yeniden eşlemek tehlikelidir ve yanlış eşlerseniz çalışmalarını durdurabilir.\n\nBir hata yaparsanız, güç ve ses düğmelerini 30 saniye basılı tutarak cihazınızı zorla yeniden başlatmanız gerekebilir — bunun nasıl yapılacağı konusunda cihazınızın kılavuzuna veya internete başvurun. - %d… - Anlıyorum - Anladım - Kurulum - Root algılandı - Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın açılışta PRO modunu otomatik olarak başlatmasını da sağlayacaktır. - PRO modunu başlat - Shizuku algılandı - Key Mapper\'a Shizuku izni vererek kurulum sürecini atlayabilirsiniz. - Shizuku\'yu başlat - İzin iste - PRO modunu başlat - Key Mapper ile kur - Devam et - Devam et (Android 11+) - Seçenekler - Tüm tuş eşlemeleri için PRO modunu etkinleştir - Key Mapper yeniden eşleme için ADB Shell\'i kullanacak - Bu ayarlar, uyarıyı kabul edene kadar kullanılamaz. - PRO modu hizmeti çalışıyor - Durdur - Açılışta otomatik olarak başlat - Cihazınızı her açtığınızda veya yeniden başlattığınızda PRO Modu kendini başlatacaktır - Acil durum ipucu - Güç düğmeniz çalışmazsa, PRO Modu\'nu devre dışı bırakmak için güç düğmesini 10 saniye basılı tutun ve bırakın. - Kurulum sihirbazı - Adım %d / %d - Etkileşimli kurulum asistanını kullan - Ayarlarla otomatik olarak etkileşime gir - Önce erişilebilirlik servisini etkinleştir - Eğitimi izle - Servisi başlat - Ayarlara git - Servisi başlat - Erişilebilirlik servisini etkinleştir - Key Mapper, PRO modunu kurmanıza yardımcı olmak için bir servis kullanır. Sıradan tuş eşlemeleri için de kullanışlıdır. - Geliştirici seçeneklerini etkinleştir - Key Mapper\'ın PRO modunu başlatmak için Android Debug Bridge\'i kullanması gerekir ve bunun için geliştirici seçeneklerini etkinleştirmeniz gerekir. - Bir WiFi ağına bağlan - Key Mapper\'ın ADB\'yi etkinleştirmek için bir WiFi ağına ihtiyacı var. İnternet bağlantısına ihtiyacınız yok.\n\nWiFi ağı yok mu? Başka birinin telefonundan bir hotspot kullanın. - Kablosuz hata ayıklamayı etkinleştir - Key Mapper, yeniden eşleme ve giriş hizmetini başlatmak için kablosuz hata ayıklamayı kullanır. - Kablosuz hata ayıklamayı eşleştir - Key Mapper\'ın yeniden eşleme ve giriş hizmetini başlatabilmesi için kablosuz hata ayıklama ile eşleşmesi gerekir. - Servisi başlat - Key Mapper\'ın PRO modu hizmetini başlatmak için Android Debug Bridge\'e bağlanması gerekir. - Bildirimlere izin ver - Key Mapper\'ın kurulum sürecinde herhangi bir sorun olması durumunda sizi bilgilendirmek için izne ihtiyacı var. - İzin ver - Kurulum asistanı - PRO modu çalışıyor - Artık ekran kapalıyken tuşları yeniden eşleyebilir ve daha fazla eylem kullanabilirsiniz. - Bitir - Geliştirici seçeneklerini etkinleştir - Yapı numarasına tekrar tekrar dokun - Otomatik olarak eşleştiriliyor - Eşleştirme kodu ve portu aranıyor… - Eşleştirme portu ve kodu bulunamadı - Eşleştirme koduyla eşleştirmek için düğmeye dokunun ve kodu buraya yazın - PRO Modu başlatılamadı - Tekrar kurmak için dokunun. Tekrar tekrar başarısız olursa ADB eşleştirmeyi ve telefonunuzu yeniden başlatmayı deneyin. - PRO modu otomatik başlatılıyor - Root kullanılıyor - Shizuku kullanılıyor - WiFi üzerinden ADB kullanılıyor - PRO modu başlatıldı - Yeniden eşlemede iyi eğlenceler! ❤️ - Eşleştirme başarısız - Eşleştirme kodunu gönderirken eşleştirme kodu açılır penceresini ekranda tutun - Eşleştirme kodunu gir - PRO modu ile ne yapabilirim? - 📲 Güç düğmeniz gibi daha fazla tuşu yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir klavyeyi kullanın.\n⭐️ Aşağıdaki eylemlerin kilidi açılır: WiFi, Bluetooth, mobil veri, NFC ve uçak modu, durum çubuğunu daralt ve cihazı uyut/uyandır. - PRO modu bilgilerini göster - Kapat + PRO modu + Kurulum + Önemli! + PRO modu ile tuşları yeniden eşlemek tehlikelidir ve yanlış eşlerseniz çalışmalarını durdurabilir.\n\nBir hata yaparsanız, güç ve ses düğmelerini 30 saniye basılı tutarak cihazınızı zorla yeniden başlatmanız gerekebilir — bunun nasıl yapılacağı konusunda cihazınızın kılavuzuna veya internete başvurun. + %d… + Anlıyorum + Anladım + Kurulum + Root algılandı + Key Mapper\'a root izni vererek kurulum sürecini atlayabilirsiniz. Bu, Key Mapper\'ın WiFi bağlantısını beklemeden PRO modunu otomatik olarak başlatmasını da sağlar. + PRO modunu başlat + Shizuku algılandı + Key Mapper\'a Shizuku izni vererek kurulum sürecini atlayabilirsiniz. + Shizuku\'yu başlat + İzin iste + PRO modunu başlat + Key Mapper ile kur + Devam et + Devam et (Android 11+) + Seçenekler + Tüm tuş eşlemeleri için PRO modunu etkinleştir + Key Mapper yeniden eşleme için ADB Shell\'i kullanacak + Bu ayarlar, uyarıyı kabul edene kadar kullanılamaz. + PRO modu hizmeti çalışıyor + Durdur + Otomatik başlat ve çalışır durumda tut + PRO Modu, cihazınızı başlattığınızda, Key Mapper\'ı açtığınızda veya beklenmedik bir şekilde kapandığında kendini başlatır. + Tuş olayı eylemleri + Tuş olayı eylemlerinin nasıl gerçekleştirileceğini seçin + Acil durum ipucu + Güç düğmeniz çalışmazsa, PRO Modu\'nu devre dışı bırakmak için güç düğmesini 10 saniye basılı tutun ve bırakın. + Kurulum sihirbazı + Adım %d / %d + Etkileşimli kurulum asistanını kullan + Ayarlarla otomatik olarak etkileşime gir + Önce erişilebilirlik servisini etkinleştir + Eğitimi izle + Servisi başlat + Ayarlara git + Servisi başlat + Erişilebilirlik servisini etkinleştir + Key Mapper bu servisi kurulumda yardımcı olması için kullanır. Ayrıca sıradan tuş eşlemeleri için de gereklidir. + Geliştirici seçeneklerini etkinleştir + Key Mapper\'ın Android Hata Ayıklama Köprüsü\'nü (ADB) kullanması gerekir, bu nedenle geliştirici seçeneklerini etkinleştirmelisiniz. + Bir WiFi ağına bağlan + Key Mapper\'ın ADB\'yi etkinleştirmek için bir WiFi ağına ihtiyacı var. İnternet bağlantısına ihtiyacınız yok.\n\nWiFi ağı yok mu? Başka birinin telefonundan bir hotspot kullanın. + Kablosuz hata ayıklamayı etkinleştir + Key Mapper, yeniden eşleme ve giriş hizmetini başlatmak için kablosuz hata ayıklamayı kullanır. + Kablosuz hata ayıklamayı eşleştir + Key Mapper\'ın yeniden eşleme ve giriş hizmetini başlatabilmesi için kablosuz hata ayıklama ile eşleşmesi gerekir. + Servisi başlat + Key Mapper\'ın servisi başlatmak için ADB\'ye bağlanması gerekir. + Bildirimlere izin ver + Key Mapper\'ın kurulum sürecinde herhangi bir sorun olması durumunda sizi bilgilendirmek için izne ihtiyacı var. + İzin ver + Uyumsuz USB yapılandırması + PRO Modunun cihazınızı her kilitlediğinizde sonlandırılmaması için varsayılan USB yapılandırmanız olarak \'Veri aktarımı yok\' seçeneğini seçmelisiniz. + Kurulum asistanı + PRO modu çalışıyor + Artık ekran kapalıyken tuşları yeniden eşleyebilir ve daha fazla eylem kullanabilirsiniz. + Bitir + Geliştirici seçeneklerini etkinleştir + Yapı numarasına tekrar tekrar dokun + Otomatik olarak eşleştiriliyor + Eşleştirme kodu ve portu aranıyor… + Eşleştirme portu ve kodu bulunamadı + Eşleştirme koduyla eşleştirmek için düğmeye dokunun ve kodu buraya yazın + PRO Modu başlatılamadı + Tekrar kurmak için dokunun. Tekrar tekrar başarısız olursa ADB eşleştirmeyi ve telefonunuzu yeniden başlatmayı deneyin. + PRO modu otomatik başlatılıyor + Root kullanılıyor + Shizuku kullanılıyor + WiFi üzerinden ADB kullanılıyor + PRO modu başlatıldı + Yeniden eşlemede iyi eğlenceler! ❤️ + Eşleştirme başarısız + Eşleştirme kodunu gönderirken eşleştirme kodu açılır penceresini ekranda tutun + Eşleştirme kodunu gir + PRO modu ile ne yapabilirim? + 📲 Güç düğmeniz gibi daha fazla düğmeyi yeniden eşleyebilirsiniz.\n⌨️ Tuş kodu eylemleriyle herhangi bir ekran klavyesini kullanabilirsiniz.\n⭐️ Ve daha birçok eylemi kullanabilirsiniz. + PRO modu bilgilerini göster + Kapat PRO modu beklenmedik şekilde durdu Otomatik olarak yeniden başlatılıyor… - Otomatik olarak yeniden başlatılmıyor. Servisi siz durdurmuyorsanız, sorunu geliştiriciye bildirin. + Son otomatik başlatma 5 dakikadan daha kısa bir süre önce olduğu için otomatik olarak yeniden başlatılmıyor. Servisi siz sonlandırmıyorsanız, sorunu geliştiriciye bildirin. Keşfet Neyi yeniden eşlemek istersiniz? @@ -1375,7 +1483,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Özel Çentik Kilit ekranı - Telefonunuzu bir üst seviyeye taşıyın + Deneyiminizi yükseltin Kayan düğmeler, herhangi bir uygulama, oyun veya kilit ekranınızdaki anlık kısayollardır. Desteğiniz Key Mapper\'ı hayatta tutar ❤️ Çalışırken görün Kapat @@ -1404,6 +1512,7 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Bu cihazı yeniden eşleyebilirsiniz Bu özelliği kullanabilirsiniz Ekran kapalıyken yeniden eşle + Ekran kapalıyken yeniden eşle Seçenekler Gereksinimler Düğmeleriniz hala algılanamıyorsa, lütfen Discord sunucumuza katılın ve bize bildirin. Düğmelerinizi yeniden eşlemenize yardımcı olmak için elimizden gelenin en iyisini yapacağız. @@ -1413,10 +1522,10 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Etkinleştir Çalışıyor Satın alındı - PRO modu - Ücretsiz etkinleştir - Çalışıyor - Bu Android sürümünde mevcut değil + PRO modu + Ücretsiz etkinleştir + Çalışıyor + Bu Android sürümünde mevcut değil Cihaz asistanınız bir donanım düğmesiyle tetikleniyorsa, PRO moduyla ücretsiz olarak yeniden eşlemek mümkün olabilir. Tetikleyici sayfasından \'Diğer\'i seçmeyi deneyin ve talimatları izleyin. Bu tetikleyici, cihaza göre değişen bazı kurulumlar gerektirir. Lütfen\ talimatları okuyun @@ -1426,9 +1535,32 @@ Kaydedilmiş ses dosyalarını ayarlardan silebilirsiniz. Yön tuşu (D-Pad) düğmesi Diğer basit düğmeler Yön tuşu (DPAD) düğmelerini yeniden eşlerken normal ekran klavyenizi kullanamayacaksınız. - Key Mapper Klavyesi - Klavyeyi etkinleştir - Klavye seç + Key Mapper giriş yöntemi + Etkinleştir + Seç Çalışıyor + + Tuş olayı eylemini düzelt + Bu eylemi kullanmak için ek adımlar vardır. Hangi yöntemi kullanmak istediğinizi seçin: + Key Mapper giriş yöntemi + Ekran klavyesi yok + Normal ekran klavyenizi her zaman kullanabilirsiniz + Uygulamalar ve oyunlarla daha iyi uyumluluk + Key Mapper giriş yöntemini etkinleştir + Key Mapper giriş yöntemini kullan + Yazarken normal ekran klavyesine otomatik olarak geç + Bunu istediğiniz zaman Ayarlar\'dan değiştirebilirsiniz + Kurulum + Seçenekler + SMS Testi + Başarıyla gönderildi! + SMS göndermek operatör veya dolaşım ücretlerine neden olabilir. Key Mapper geliştiricileri herhangi bir maliyetten sorumlu değildir. + Açıklama + Zaman aşımı + + Kayan düğmeleri beğendiniz mi? + Görüşlerinizi merak ediyoruz! Lütfen başkalarının da bu özelliği keşfetmesine yardımcı olmak için Google Play\'de bir yorum bırakmayı düşünün. ❤️ + Kapat + Evdev olayı yazılamadı diff --git a/base/src/main/res/values/strings.xml b/base/src/main/res/values/strings.xml index 4286af0b29..1d70f24dac 100644 --- a/base/src/main/res/values/strings.xml +++ b/base/src/main/res/values/strings.xml @@ -77,8 +77,8 @@ Too many fingers to perform gesture due to android limitations. Gesture duration is too high due to android limitations. You must be using the Key Mapper Input Method for DPAD triggers to work! - PRO mode is unsupported on this Android version - PRO mode not started! + Expert Mode is unsupported on this Android version + Expert Mode not started! Trigger device not connected! Migrate this screen off trigger @@ -256,6 +256,12 @@ Landscape (270°) Portrait (any) Landscape (any) + Screen orientation + Physical orientation + Physical: Portrait + Physical: Landscape + Physical: Portrait (upside down) + Physical: Landscape (inverted) App playing media App not playing media Media is playing @@ -401,7 +407,7 @@ Add action Tap to record trigger - Advanced triggers + Record with Expert Mode NEW! Done Fix @@ -431,9 +437,9 @@ Please grant Key Mapper root permission in your root management app, such as Magisk. Grant WRITE_SECURE_SETTINGS permission - You will need to use PRO mode to grant this permission. + You will need to use Expert Mode to grant this permission. - Your device doesn\'t seem to have an accessibility services settings page. Tap \"guide\" to read the online guide that explains how to fix this. + Your device doesn\'t seem to have an accessibility services settings page. You can set up Expert Mode or run the ADB command \"adb shell pm grant io.github.sds100.keymapper android.permission.WRITE_SECURE_SETTINGS\". You must hold down the keys in the order that they are listed. There is a timeout to input this trigger. You can change this timeout in the "Options" tab. How to use this trigger @@ -482,16 +488,16 @@ Some actions and options need this permission to work. You can also get notified when there is important news about the app. Migrate this screen off trigger - We\'re sorry for the disruption. We\'ve introduced a *free* feature called PRO mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with PRO mode. + We\'re sorry for the disruption. We\'ve introduced a *free* feature called Expert Mode that replaces the old screen off remapping option. This feature is much more reliable and unlocks remapping the power button too.\n\nDue to the way it works you will need to record this trigger again with Expert Mode. Proceed Grant READ_LOGS permission - You will need to be rooted or have enabled PRO mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. + You will need to be rooted or have enabled Expert Mode for this to work. The system will then close the app when it grants this permission. You will need to open it again yourself and try sharing the logcat again. Done Kill - Guide + Use Expert Mode Change Fix partially Ok @@ -588,7 +594,7 @@ Change automatic backup location Turn on automatic backup - Periodically back up your key maps + Back up after modifying key maps Automatically change the on-screen keyboard when a device (e.g a keyboard) connects/disconnects The Key Mapper Input Method will be automatically selected when a chosen device is connected. Your normal keyboard will be automatically selected when the device disconnects. @@ -677,8 +683,8 @@ Logging This may add latency to your key maps so only turn this on if you are trying to debug the app or have been asked to by the developer. - Use PRO mode - Advanced detection of key events and more + Use Expert Mode + Advanced detection of key events and more Light Dark @@ -888,7 +894,7 @@ UI element not found! Command timed out after %1$d seconds - PRO Mode needs starting + Expert Mode needs starting Rate limit reached. You can only send once per second. @@ -1138,14 +1144,15 @@ Shell command action Script Script cannot be empty! + Do not put \'adb shell\'! Run as root Standard Root ADB Execution Mode ADB mode does not support streaming output - Setup PRO Mode - Setup PRO Mode (Unsupported) + Setup Expert Mode + Setup Expert Mode (Unsupported) Configuration Output No output yet. Click Test to run the command. @@ -1230,6 +1237,21 @@ Special + + Apps + Media + Bluetooth + Display + Flashlight + WiFi + Keyboard + Lock + Phone + Power + Device + Time + + Boolean Boolean array @@ -1366,8 +1388,8 @@ Something went wrong 😕 Retry Contact developer - You must purchase the side key trigger feature! Tap on the key map and then purchase it by clicking on \'Advanced triggers\'. - You must purchase the floating buttons feature! Tap on the key map and then purchase it by clicking on \'Advanced triggers\'. + You must purchase the side key trigger feature! Tap on the key map and then purchase it by clicking on the shop. + You must purchase the floating buttons feature! Tap on the key map and then purchase it by clicking on the shop. contact@keymapper.club Key Mapper Pro query Please fill the following information so I can help you.\n\n1. Device model:\n2. Android version:\n3. Key maps (make a back up in the home screen menu):\n4. Screenshot of Key Mapper home screen:\n5. Describe the problem you are having: @@ -1409,19 +1431,19 @@ Floating buttons display over the apps you want. They work just like real buttons, and you can place, style, and map them however you like. Floating button %s (%s) Deleted floating button - Better Caps Lock compatibility - PRO mode provides better compatibility for Caps Lock remapping. Tap \'Use PRO mode\' and record it again. - Use PRO mode - Screen off remapping? - Any trigger can now work when the screen is off for free with PRO mode! You will need to record it again. - Use PRO mode + Better Caps Lock compatibility + Expert Mode provides better compatibility for Caps Lock remapping. Tap \'Use Expert Mode\' and record it again. + Use Expert Mode + Screen off remapping? + Any trigger can now work when the screen is off for free with Expert Mode! You will need to record it again. + Use Expert Mode App pinning warning Using the back button as a trigger may conflict with app pinning, causing a black screen on unlock. A reboot will fix it. Keyboard icon The ⌨ symbol means you must use the Key Mapper input method for this trigger to work due to an Android restriction. Ringer mode actions - Consider using PRO mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. - Use PRO mode + Consider using Expert Mode for ringer mode actions to avoid conflicts with Do Not Disturb settings. + Use Expert Mode Limit to specific apps? Add constraints in the Constraints tab. Choose a layout @@ -1470,7 +1492,7 @@ Yes, delete Cancel Hide floating layouts - You can find floating buttons in the Advanced Triggers button when creating a trigger. + You can find floating buttons in the Shop button when creating a trigger. Floating buttons Menu Sort @@ -1555,7 +1577,6 @@ Swipe down fingerprint reader Swipe left fingerprint reader Swipe right fingerprint reader - Advanced triggers Key code %d Scan code %d Scan code @@ -1564,7 +1585,7 @@ Use key code %d Use scan code %d No scan code saved - Use PRO mode + Use Expert Mode Add more @@ -1600,6 +1621,7 @@ Options Unsupported + Unsupported Add constraints if you want key maps to only work in some situations. @@ -1630,115 +1652,116 @@ - - PRO mode - Important! - Remapping buttons with PRO mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. - %d… - I understand - Understood - Set up - Root detected - You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start PRO mode on boot as well. - Start PRO mode - Shizuku detected - You can skip the set up process by giving Key Mapper Shizuku permission. - Start Shizuku - Request permission - Start PRO mode - Set up with Key Mapper - Continue - Continue (Android 11+) - Options - Enable PRO mode for all key maps - Key Mapper will use the ADB Shell for remapping - These settings are unavailable until you acknowledge the warning. - PRO mode service is running - Stop - - Automatically start at boot - PRO Mode will start itself whenever you turn on or restart your device + + Expert Mode + Setup + Important! + Remapping buttons with Expert Mode is dangerous and can cause them to stop working if you remap them incorrectly.\n\nIf you make a mistake, you may need to force restart your device by holding down the power and volume buttons for 30 seconds — consult your device\'s manual or the internet for how to do this. + %d… + I understand + Understood + Set up + Root detected + You can skip the set up process by giving Key Mapper root permission. This will let Key Mapper auto start Expert Mode without having to wait for a WiFi connection as well. + Start Expert Mode + Shizuku detected + You can skip the set up process by giving Key Mapper Shizuku permission. + Start Shizuku + Request permission + Start Expert Mode + Set up with Key Mapper + Continue + (Requires Android 11+) + Options + Enable Expert Mode for all key maps + Key Mapper will use the ADB Shell for remapping + These settings are unavailable until you acknowledge the warning. + Expert Mode service is running + Stop + + Auto start and keep alive + Expert Mode will start itself whenever you boot your device or it dies unexpectedly. Key event actions Select how key event actions are performed - Emergency tip - If your power button stops working, hold down the power button for 10 seconds and release to disable PRO Mode. + Emergency tip + If your power button stops working, hold down the power button for 10 seconds and release to disable Expert Mode. - Setup wizard - Step %d of %d - Use interactive setup assistant - Automatically interact with settings - Enable accessibility service first - Watch tutorial - Start service - Go to settings - Start service + Setup wizard + Step %d of %d + Use interactive setup assistant + Automatically interact with settings + Enable accessibility service first + Watch tutorial + Start service + Go to settings + Start service - Enable accessibility service - Key Mapper uses a service to help you set up PRO mode. It\'s also useful for ordinary key maps. + Enable accessibility service + Key Mapper uses this service to help with setup. It\'s also needed for ordinary key maps. - Enable developer options - Key Mapper needs to use Android Debug Bridge to start PRO mode, and you need to enable developer options for that. + Enable developer options + Key Mapper needs to use Android Debug Bridge (ADB) so you need to enable developer options. - Connect to a WiFi network - Key Mapper needs a WiFi network to enable ADB. You do not need an internet connection.\n\nNo WiFi network? Use a hotspot from someone else\'s phone. + Connect to a WiFi network + Key Mapper needs a WiFi network to enable ADB. You do not need an internet connection.\n\nNo WiFi network? Use a hotspot from someone else\'s phone. - Enable wireless debugging - Key Mapper uses wireless debugging to launch its remapping and input service. + Enable wireless debugging + Key Mapper uses wireless debugging to launch its remapping and input service. - Pair wireless debugging - Key Mapper needs to pair with wireless debugging before it can launch its remapping and input service. + Pair wireless debugging + Key Mapper needs to pair with wireless debugging before it can launch its remapping and input service. - Start service - Key Mapper needs to connect to the Android Debug Bridge to start the PRO mode service. + Start service + Key Mapper needs to connect to ADB to start the service. - Allow notifications - Key Mapper needs permission to notify you if there are any issues with the set up process. - Give permission + Allow notifications + Key Mapper needs permission to notify you if there are any issues with the set up process. + Give permission - Incompatible USB configuration - You must select \'No data transfer\' as your default USB configuration so that PRO Mode is not killed every time you lock your device. + Incompatible USB configuration + You must select \'No data transfer\' as your default USB configuration so that Expert Mode is not killed every time you lock your device. - Setup assistant + Setup assistant - PRO mode is running - You can now remap buttons when the screen is off and use more actions. + Expert Mode is running + You can now remap buttons when the screen is off and use more actions. - Finish + Finish - Enable developer options - Tap build number repeatedly + Enable developer options + Tap build number repeatedly - Pairing automatically - Searching for pairing code and port… + Pairing automatically + Searching for pairing code and port… - Unable to find pairing port and code - Tap on the button to pair with pairing code and type the code in here + Unable to find pairing port and code + Tap on the button to pair with pairing code and type the code in here - Starting PRO Mode failed - Tap to set up again. Try ADB pairing and rebooting your phone if it repeatedly fails. + Starting Expert Mode failed + Tap to set up again. Try ADB pairing and rebooting your phone if it repeatedly fails. - Auto starting PRO mode - Using root - Using shizuku - Using ADB over WiFi + Auto starting Expert Mode + Using root + Using shizuku + Using ADB over WiFi - PRO mode started - Have fun remapping! ❤️ + Expert Mode started + Have fun remapping! ❤️ - Pairing failed - Keep the pairing code popup on-screen when submitting the pairing code - Input pairing code + Pairing failed + Keep the pairing code popup on-screen when submitting the pairing code + Input pairing code - What can I do with PRO mode? - 📲 You can remap more buttons, such as your power button.\n⌨️ Use any keyboard with key code actions.\n⭐️ The following actions are unlocked: WiFi, Bluetooth, mobile data, NFC, and airplane mode, collapse status bar, and sleep/wake device. - Show PRO mode info - Dismiss + What can I do with Expert Mode? + 📲 You can remap more buttons, such as your power button.\n⌨️ Use any on-screen keyboard with key code actions.\n⭐️ And use many more actions. + Show Expert Mode info + Dismiss - PRO mode stopped unexpectedly + Expert Mode stopped unexpectedly Automatically restarting… - Not auto restarting. If you\'re not killing the service report the issue to the developer. + Not auto restarting because last auto started less than 5 minutes ago. If you\'re not killing the service report the issue to the developer. Discover @@ -1799,16 +1822,16 @@ Enable Running Purchased - PRO mode - Enable for free - Running - Not available on this Android version - If your device assistant is triggered by a hardware button, it might be possible to remap it for free with PRO mode. Try choosing \'Other\' from the trigger page and follow the instructions. + Expert Mode + Enable for free + Running + Not available on this Android version + If your device assistant is triggered by a hardware button, it might be possible to remap it for free with Expert Mode. Try choosing \'Other\' from the trigger page and follow the instructions. This trigger requires some set up that varies per device. Please\ read the instructions Read instructions Select trigger type - If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use PRO mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. + If your device assistant is triggered by the power button, it might be possible to remap the assistant instead, meaning you don\'t need to use Expert Mode. Try choosing \'Assistant\' from the trigger page and follow the instructions. D-Pad button Other simple buttons You will not be able to use your normal on-screen keyboard when remapping DPAD buttons. @@ -1823,8 +1846,8 @@ There are extra steps to use this action. Select which method you want to use: Key Mapper input method No on-screen keyboard - Can use your normal on-screen keyboard at all times - Better compatibility with apps and games + Can use your normal on-screen keyboard at all times + Better compatibility with apps and games Enable Key Mapper input method Use Key Mapper input method Automatically switch to normal on-screen keyboard when typing @@ -1842,5 +1865,6 @@ Are you enjoying floating buttons? We would love to know! Please consider leaving a review on Google Play to help others discover this feature. ❤️ Dismiss + Write evdev event failed diff --git a/base/src/test/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModelTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModelTest.kt new file mode 100644 index 0000000000..558dfe2361 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/actions/ConfigShellCommandViewModelTest.kt @@ -0,0 +1,267 @@ +package io.github.sds100.keymapper.base.actions + +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import io.github.sds100.keymapper.base.R +import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository +import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider +import io.github.sds100.keymapper.base.utils.ui.FakeResourceProvider +import io.github.sds100.keymapper.common.models.ShellExecutionMode +import io.github.sds100.keymapper.common.models.ShellResult +import io.github.sds100.keymapper.common.utils.Success +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.flow.flowOf +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.resetMain +import kotlinx.coroutines.test.runTest +import kotlinx.coroutines.test.setMain +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.hamcrest.Matchers.nullValue +import org.junit.After +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.times +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@Suppress("UnusedFlow") +@ExperimentalCoroutinesApi +@RunWith(MockitoJUnitRunner::class) +class ConfigShellCommandViewModelTest { + + @get:Rule + var instantExecutorRule = InstantTaskExecutorRule() + + private val testDispatcher = UnconfinedTestDispatcher() + + private lateinit var viewModel: ConfigShellCommandViewModel + private lateinit var mockExecuteShellCommandUseCase: ExecuteShellCommandUseCase + private lateinit var mockNavigationProvider: NavigationProvider + private lateinit var mockSystemBridgeConnectionManager: SystemBridgeConnectionManager + private lateinit var fakePreferenceRepository: FakePreferenceRepository + private lateinit var fakeResourceProvider: FakeResourceProvider + + private val commandEmptyErrorString = "Command cannot be empty" + private val descriptionEmptyErrorString = "Description cannot be empty" + + @Before + fun setUp() { + Dispatchers.setMain(testDispatcher) + + fakeResourceProvider = FakeResourceProvider().apply { + stringResourceMap[R.string.action_shell_command_command_empty_error] = + commandEmptyErrorString + stringResourceMap[R.string.error_cant_be_empty] = descriptionEmptyErrorString + } + + mockExecuteShellCommandUseCase = mock() + mockNavigationProvider = mock() + mockSystemBridgeConnectionManager = mock() + + fakePreferenceRepository = FakePreferenceRepository() + + viewModel = ConfigShellCommandViewModel( + executeShellCommandUseCase = mockExecuteShellCommandUseCase, + navigationProvider = mockNavigationProvider, + systemBridgeConnectionManager = mockSystemBridgeConnectionManager, + preferenceRepository = fakePreferenceRepository, + resourceProvider = fakeResourceProvider, + ) + } + + @After + fun tearDown() { + Dispatchers.resetMain() + } + + @Test + fun `when clicking done show error when command is blank`() = runTest { + viewModel.onCommandChanged("") + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + assertThat(viewModel.state.descriptionError, `is`(nullValue())) + verify(mockNavigationProvider, never()).popBackStackWithResult(any()) + } + + @Test + fun `when clicking done show error when description is blank`() = runTest { + viewModel.onCommandChanged("echo test") + viewModel.onDescriptionChanged("") + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.descriptionError, `is`(descriptionEmptyErrorString)) + assertThat(viewModel.state.commandError, `is`(nullValue())) + verify(mockNavigationProvider, never()).popBackStackWithResult(any()) + } + + @Test + fun `when clicking done show error when both command and description are blank`() = runTest { + viewModel.onCommandChanged("") + viewModel.onDescriptionChanged("") + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + assertThat(viewModel.state.descriptionError, `is`(nullValue())) + verify(mockNavigationProvider, never()).popBackStackWithResult(any()) + } + + @Test + fun `when clicking done with valid command and description navigate with result`() = runTest { + viewModel.onCommandChanged("echo test") + viewModel.onDescriptionChanged("Test command") + viewModel.onTimeoutChanged(5) + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(true)) + assertThat(viewModel.state.commandError, `is`(nullValue())) + assertThat(viewModel.state.descriptionError, `is`(nullValue())) + verify(mockNavigationProvider).popBackStackWithResult(any()) + } + + @Test + fun `when clicking test show error when command is blank`() = runTest { + viewModel.onCommandChanged("") + + val result = viewModel.onTestClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + assertThat(viewModel.state.isRunning, `is`(false)) + verify( + mockExecuteShellCommandUseCase, + never(), + ).executeWithStreamingOutput(any(), any(), any()) + } + + @Test + fun `when clicking test with valid command start execution`() = runTest { + whenever( + mockExecuteShellCommandUseCase.executeWithStreamingOutput(any(), any(), any()), + ).thenReturn(flowOf(Success(ShellResult(stdout = "", exitCode = 0)))) + + viewModel.onCommandChanged("echo test") + + val result = viewModel.onTestClick() + + advanceUntilIdle() + + verify( + mockExecuteShellCommandUseCase, + times(1), + ).executeWithStreamingOutput(eq("echo test"), eq(ShellExecutionMode.STANDARD), eq(10000)) + assertThat(result, `is`(true)) + assertThat(viewModel.state.commandError, `is`(nullValue())) + assertThat(viewModel.state.isRunning, `is`(false)) + } + + @Test + fun `when changing description clear error`() = runTest { + viewModel.onDescriptionChanged("") + viewModel.onCommandChanged("ls") + viewModel.onDoneClick() + assertThat(viewModel.state.descriptionError, `is`(descriptionEmptyErrorString)) + + viewModel.onDescriptionChanged("New description") + + assertThat(viewModel.state.descriptionError, `is`(nullValue())) + assertThat(viewModel.state.description, `is`("New description")) + } + + @Test + fun `when changing description update description`() = runTest { + viewModel.onDescriptionChanged("Test description") + + assertThat(viewModel.state.description, `is`("Test description")) + assertThat(viewModel.state.descriptionError, `is`(nullValue())) + } + + @Test + fun `when changing command clear error`() = runTest { + viewModel.onCommandChanged("") + viewModel.onTestClick() + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + + viewModel.onCommandChanged("echo test") + + assertThat(viewModel.state.commandError, `is`(nullValue())) + assertThat(viewModel.state.command, `is`("echo test")) + } + + @Test + fun `when changing command update command`() = runTest { + assertThat(viewModel.state.command, `is`("")) + + viewModel.onCommandChanged("echo hello") + + assertThat(viewModel.state.command, `is`("echo hello")) + assertThat(viewModel.state.commandError, `is`(nullValue())) + } + + @Test + fun `when changing command save script text to preferences`() = runTest { + val testCommand = "echo test command" + + viewModel.onCommandChanged(testCommand) + + val savedScriptText = fakePreferenceRepository.get(Keys.shellCommandScriptText) + .first() + assertThat(savedScriptText, `is`(org.hamcrest.Matchers.notNullValue())) + } + + @Test + fun `when clicking done show error when command is whitespace`() = runTest { + viewModel.onCommandChanged(" ") + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + verify(mockNavigationProvider, never()).popBackStackWithResult(any()) + } + + @Test + fun `when clicking test show error when command is whitespace`() = runTest { + viewModel.onCommandChanged(" ") + + val result = viewModel.onTestClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.commandError, `is`(commandEmptyErrorString)) + verify( + mockExecuteShellCommandUseCase, + never(), + ).executeWithStreamingOutput(any(), any(), any()) + } + + @Test + fun `when clicking done show error when description is whitespace`() = runTest { + viewModel.onCommandChanged("echo test") + viewModel.onDescriptionChanged(" ") + + val result = viewModel.onDoneClick() + + assertThat(result, `is`(false)) + assertThat(viewModel.state.descriptionError, `is`(descriptionEmptyErrorString)) + verify(mockNavigationProvider, never()).popBackStackWithResult(any()) + } +} diff --git a/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt index 71c4cd37f2..c1e2cfc7eb 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/actions/GetActionErrorUseCaseTest.kt @@ -96,7 +96,7 @@ class GetActionErrorUseCaseTest { val connectionState = if (isSystemBridgeConnected) { SystemBridgeConnectionState.Connected(time = 0L) } else { - SystemBridgeConnectionState.Disconnected(time = 0L, isExpected = true) + SystemBridgeConnectionState.Disconnected(time = 0L, isStoppedByUser = true) } whenever(mockSystemBridgeConnectionManager.connectionState).then { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCaseTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCaseTest.kt index 7f2151377e..3f7941a56d 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCaseTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformActionsUseCaseTest.kt @@ -1,19 +1,11 @@ package io.github.sds100.keymapper.base.actions -import android.view.InputDevice -import android.view.KeyEvent -import io.github.sds100.keymapper.base.input.InjectKeyEventModel import io.github.sds100.keymapper.base.input.InputEventHub import io.github.sds100.keymapper.base.system.accessibility.IAccessibilityService import io.github.sds100.keymapper.base.system.devices.FakeDevicesAdapter -import io.github.sds100.keymapper.common.utils.InputDeviceInfo -import io.github.sds100.keymapper.common.utils.InputEventAction import io.github.sds100.keymapper.common.utils.KMError -import io.github.sds100.keymapper.common.utils.State -import io.github.sds100.keymapper.common.utils.Success import io.github.sds100.keymapper.system.popup.ToastAdapter import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.runBlocking import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.UnconfinedTestDispatcher import kotlinx.coroutines.test.runTest @@ -26,7 +18,6 @@ import org.mockito.kotlin.anyOrNull import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.never -import org.mockito.kotlin.times import org.mockito.kotlin.verify import org.mockito.kotlin.whenever @@ -48,9 +39,7 @@ class PerformActionsUseCaseTest { fakeDevicesAdapter = FakeDevicesAdapter() mockAccessibilityService = mock() mockToastAdapter = mock() - mockInputEventHub = mock { - on { runBlocking { injectKeyEvent(any(), any()) } }.then { Success(Unit) } - } + mockInputEventHub = mock() useCase = PerformActionsUseCaseImpl( service = mockAccessibilityService, @@ -113,249 +102,4 @@ class PerformActionsUseCaseTest { // THEN verify(mockToastAdapter, never()).show(anyOrNull()) } - - /** - * issue #772 - */ - @Test - fun `set the device id of key event actions to a connected game controller if is a game pad key code`() = - runTest(testDispatcher) { - // GIVEN - val fakeGamePad = InputDeviceInfo( - descriptor = "game_pad", - name = "Game pad", - id = 1, - isExternal = true, - isGameController = true, - sources = InputDevice.SOURCE_GAMEPAD, - ) - - fakeDevicesAdapter.connectedInputDevices.value = State.Data(listOf(fakeGamePad)) - - val action = ActionData.InputKeyEvent( - keyCode = KeyEvent.KEYCODE_BUTTON_A, - device = null, - ) - - // WHEN - useCase.perform(action) - - // THEN - val expectedDownEvent = InjectKeyEventModel( - keyCode = KeyEvent.KEYCODE_BUTTON_A, - action = KeyEvent.ACTION_DOWN, - metaState = 0, - deviceId = fakeGamePad.id, - scanCode = 0, - repeatCount = 0, - source = InputDevice.SOURCE_GAMEPAD, - ) - - val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) - - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) - } - - /** - * issue #772 - */ - @Test - fun `don't set the device id of key event actions to a connected game controller if there are no connected game controllers`() = - runTest(testDispatcher) { - // GIVEN - fakeDevicesAdapter.connectedInputDevices.value = State.Data(emptyList()) - - val action = ActionData.InputKeyEvent( - keyCode = KeyEvent.KEYCODE_BUTTON_A, - device = null, - ) - - // WHEN - useCase.perform(action) - - // THEN - val expectedDownEvent = InjectKeyEventModel( - - keyCode = KeyEvent.KEYCODE_BUTTON_A, - action = KeyEvent.ACTION_DOWN, - metaState = 0, - deviceId = 0, - scanCode = 0, - repeatCount = 0, - source = InputDevice.SOURCE_GAMEPAD, - ) - - val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) - - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) - } - - /** - * issue #772 - */ - @Test - fun `don't set the device id of key event actions to a connected game controller if the action has a custom device set`() = - runTest(testDispatcher) { - // GIVEN - val fakeGamePad = InputDeviceInfo( - descriptor = "game_pad", - name = "Game pad", - id = 1, - isExternal = true, - isGameController = true, - sources = InputDevice.SOURCE_GAMEPAD, - ) - - val fakeKeyboard = InputDeviceInfo( - descriptor = "keyboard", - name = "Keyboard", - id = 2, - isExternal = true, - isGameController = false, - sources = InputDevice.SOURCE_GAMEPAD, - ) - - fakeDevicesAdapter.connectedInputDevices.value = - State.Data(listOf(fakeGamePad, fakeKeyboard)) - - val action = ActionData.InputKeyEvent( - keyCode = KeyEvent.KEYCODE_BUTTON_A, - device = ActionData.InputKeyEvent.Device( - descriptor = "keyboard", - name = "Keyboard", - ), - ) - - // WHEN - useCase.perform(action) - - // THEN - val expectedDownEvent = InjectKeyEventModel( - - keyCode = KeyEvent.KEYCODE_BUTTON_A, - action = KeyEvent.ACTION_DOWN, - metaState = 0, - deviceId = fakeKeyboard.id, - scanCode = 0, - repeatCount = 0, - source = InputDevice.SOURCE_GAMEPAD, - ) - - val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) - - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) - } - - /** - * issue #637 - */ - @Test - fun `perform key event action with device name and multiple devices connected with same descriptor and none support the key code, ensure action is still performed`() = - runTest(testDispatcher) { - // GIVEN - val descriptor = "fake_device_descriptor" - - val action = ActionData.InputKeyEvent( - keyCode = 1, - metaState = 0, - device = ActionData.InputKeyEvent.Device( - descriptor = descriptor, - name = "fake_name_2", - ), - ) - - fakeDevicesAdapter.connectedInputDevices.value = State.Data( - listOf( - InputDeviceInfo( - descriptor = descriptor, - name = "fake_name_1", - id = 10, - isExternal = true, - isGameController = false, - sources = InputDevice.SOURCE_GAMEPAD, - ), - - InputDeviceInfo( - descriptor = descriptor, - name = "fake_name_2", - id = 11, - isExternal = true, - isGameController = false, - sources = InputDevice.SOURCE_GAMEPAD, - ), - ), - ) - - // none of the devices support the key code - fakeDevicesAdapter.deviceHasKey = { id, keyCode -> false } - - // WHEN - useCase.perform(action, inputEventAction = InputEventAction.DOWN_UP, keyMetaState = 0) - - // THEN - val expectedDownEvent = InjectKeyEventModel( - - keyCode = 1, - action = KeyEvent.ACTION_DOWN, - metaState = 0, - deviceId = 11, - scanCode = 0, - repeatCount = 0, - source = InputDevice.SOURCE_KEYBOARD, - ) - - val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) - - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) - } - - @Test - fun `perform key event action with no device name, ensure action is still performed with correct device id`() = - runTest(testDispatcher) { - // GIVEN - val descriptor = "fake_device_descriptor" - - val action = ActionData.InputKeyEvent( - keyCode = 1, - metaState = 0, - device = ActionData.InputKeyEvent.Device(descriptor = descriptor, name = ""), - ) - - fakeDevicesAdapter.connectedInputDevices.value = State.Data( - listOf( - InputDeviceInfo( - descriptor = descriptor, - name = "fake_name", - id = 10, - isExternal = true, - isGameController = false, - sources = InputDevice.SOURCE_GAMEPAD, - ), - ), - ) - - // WHEN - useCase.perform(action, inputEventAction = InputEventAction.DOWN_UP, keyMetaState = 0) - - // THEN - val expectedDownEvent = InjectKeyEventModel( - - keyCode = 1, - action = KeyEvent.ACTION_DOWN, - metaState = 0, - deviceId = 10, - scanCode = 0, - repeatCount = 0, - source = InputDevice.SOURCE_KEYBOARD, - ) - - val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) - - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) - verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) - } } diff --git a/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegateTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegateTest.kt new file mode 100644 index 0000000000..9cb56943b0 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/actions/PerformKeyEventActionDelegateTest.kt @@ -0,0 +1,577 @@ +package io.github.sds100.keymapper.base.actions + +import android.view.InputDevice +import android.view.KeyEvent +import io.github.sds100.keymapper.base.input.InjectKeyEventModel +import io.github.sds100.keymapper.base.input.InputEventHub +import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository +import io.github.sds100.keymapper.base.system.devices.FakeDevicesAdapter +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.utils.InputDeviceInfo +import io.github.sds100.keymapper.common.utils.InputEventAction +import io.github.sds100.keymapper.common.utils.State +import io.github.sds100.keymapper.common.utils.Success +import io.github.sds100.keymapper.data.Keys +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.runTest +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.Mockito.never +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.mock +import org.mockito.kotlin.times +import org.mockito.kotlin.verify + +@ExperimentalCoroutinesApi +@RunWith(MockitoJUnitRunner::class) +class PerformKeyEventActionDelegateTest { + + companion object { + private val FAKE_CONTROLLER_EVDEV_DEVICE = EvdevDeviceInfo( + name = "Fake Controller", + bus = 1, + vendor = 2, + product = 1, + ) + } + + private val testDispatcher = UnconfinedTestDispatcher() + private val testCoroutineScope = TestScope(testDispatcher) + private lateinit var delegate: PerformKeyEventActionDelegate + private lateinit var fakePreferenceRepository: FakePreferenceRepository + private lateinit var mockInputEventHub: InputEventHub + + private lateinit var fakeDevicesAdapter: FakeDevicesAdapter + + @Before + fun init() { + fakePreferenceRepository = FakePreferenceRepository() + fakeDevicesAdapter = FakeDevicesAdapter() + + mockInputEventHub = mock { + on { runBlocking { injectKeyEvent(any(), any()) } }.then { Success(Unit) } + on { + runBlocking { injectEvdevEventKeyCode(any(), any(), any()) } + }.then { Success(Unit) } + } + + delegate = PerformKeyEventActionDelegate( + coroutineScope = testCoroutineScope, + settingsRepository = fakePreferenceRepository, + inputEventHub = mockInputEventHub, + devicesAdapter = fakeDevicesAdapter, + ) + } + + @Test + fun `inject evdev event if action device set as a non-evdev device but it is disconnected`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = ActionData.InputKeyEvent.Device( + descriptor = "keyboard_descriptor", + name = "Keyboard", + ), + ) + + // The Keyboard is not connected, so still perform the action through the evdev device + // that triggered it. + fakeDevicesAdapter.connectedInputDevices.value = State.Data(emptyList()) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 1), + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 1, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 1, + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 1, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 0, + ) + } + + @Test + fun `do not inject evdev event if triggered by evdev device and action device is set`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + fakeDevicesAdapter.connectedInputDevices.value = State.Data( + listOf( + InputDeviceInfo( + descriptor = "keyboard_descriptor", + name = "Keyboard", + id = 10, + isExternal = true, + isGameController = false, + sources = InputDevice.SOURCE_KEYBOARD, + ), + ), + ) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_A, + device = ActionData.InputKeyEvent.Device( + descriptor = "keyboard_descriptor", + name = "Keyboard", + ), + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 1), + ) + + val expectedDownEvent = InjectKeyEventModel( + keyCode = KeyEvent.KEYCODE_A, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = 10, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_KEYBOARD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub).injectKeyEvent(expectedDownEvent, true) + verify(mockInputEventHub).injectKeyEvent(expectedUpEvent, true) + verify(mockInputEventHub, never()).injectEvdevEventKeyCode(any(), any(), any()) + } + + @Test + fun `inject evdev event if triggered by evdev device and action device set to the same device`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = ActionData.InputKeyEvent.Device( + descriptor = "descriptor", + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + ), + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 1), + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 1, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 1, + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 1, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 0, + ) + } + + @Test + fun `inject evdev event if triggered by evdev device and action device not set`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 0, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 1, + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 0, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 0, + ) + } + + @Test + fun `do not inject evdev event if not using system bridge for key event actions`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, false) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) + + val expectedDownEvent = InjectKeyEventModel( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = 0, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_GAMEPAD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub).injectKeyEvent(expectedUpEvent, false) + verify(mockInputEventHub, never()).injectEvdevEventKeyCode(any(), any(), any()) + } + + @Test + fun `inject down evdev event if triggered by evdev device and action device not set`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 0, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 1, + ) + } + + @Test + fun `inject up evdev event if triggered by evdev device and action device not set`() = + runTest(testDispatcher) { + fakePreferenceRepository.set(Keys.keyEventActionsUseSystemBridge, true) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + delegate.perform( + action, + inputEventAction = InputEventAction.UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) + + verify(mockInputEventHub).injectEvdevEventKeyCode( + deviceId = 0, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + value = 0, + ) + } + + /** + * issue #772 + */ + @Test + fun `set the device id of key event actions to a connected game controller if is a game pad key code`() = + runTest(testDispatcher) { + // GIVEN + val fakeGamePad = InputDeviceInfo( + descriptor = "game_pad", + name = "Game pad", + id = 1, + isExternal = true, + isGameController = true, + sources = InputDevice.SOURCE_GAMEPAD, + ) + + fakeDevicesAdapter.connectedInputDevices.value = + State.Data(listOf(fakeGamePad)) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + // WHEN + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Default, + ) + + // THEN + val expectedDownEvent = InjectKeyEventModel( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = fakeGamePad.id, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_GAMEPAD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) + } + + /** + * issue #772 + */ + @Test + fun `don't set the device id of key event actions to a connected game controller if there are no connected game controllers`() = + runTest(testDispatcher) { + // GIVEN + fakeDevicesAdapter.connectedInputDevices.value = + State.Data(emptyList()) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = null, + ) + + // WHEN + + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Default, + ) + + // THEN + val expectedDownEvent = InjectKeyEventModel( + + keyCode = KeyEvent.KEYCODE_BUTTON_A, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = 0, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_GAMEPAD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) + } + + /** + * issue #772 + */ + @Test + fun `don't set the device id of key event actions to a connected game controller if the action has a custom device set`() = + runTest(testDispatcher) { + // GIVEN + val fakeGamePad = InputDeviceInfo( + descriptor = "game_pad", + name = "Game pad", + id = 1, + isExternal = true, + isGameController = true, + sources = InputDevice.SOURCE_GAMEPAD, + ) + + val fakeKeyboard = InputDeviceInfo( + descriptor = "keyboard", + name = "Keyboard", + id = 2, + isExternal = true, + isGameController = false, + sources = InputDevice.SOURCE_GAMEPAD, + ) + + fakeDevicesAdapter.connectedInputDevices.value = + State.Data(listOf(fakeGamePad, fakeKeyboard)) + + val action = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_BUTTON_A, + device = ActionData.InputKeyEvent.Device( + descriptor = "keyboard", + name = "Keyboard", + ), + ) + + // WHEN + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Default, + ) + + // THEN + val expectedDownEvent = InjectKeyEventModel( + + keyCode = KeyEvent.KEYCODE_BUTTON_A, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = fakeKeyboard.id, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_GAMEPAD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) + } + + /** + * issue #637 + */ + @Test + fun `perform key event action with device name and multiple devices connected with same descriptor and none support the key code, ensure action is still performed`() = + runTest(testDispatcher) { + // GIVEN + val descriptor = "fake_device_descriptor" + + val action = ActionData.InputKeyEvent( + keyCode = 1, + metaState = 0, + device = ActionData.InputKeyEvent.Device( + descriptor = descriptor, + name = "fake_name_2", + ), + ) + + fakeDevicesAdapter.connectedInputDevices.value = + State.Data( + listOf( + InputDeviceInfo( + descriptor = descriptor, + name = "fake_name_1", + id = 10, + isExternal = true, + isGameController = false, + sources = InputDevice.SOURCE_GAMEPAD, + ), + + InputDeviceInfo( + descriptor = descriptor, + name = "fake_name_2", + id = 11, + isExternal = true, + isGameController = false, + sources = InputDevice.SOURCE_GAMEPAD, + ), + ), + ) + + // none of the devices support the key code + fakeDevicesAdapter.deviceHasKey = { id, keyCode -> false } + + // WHEN + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Default, + ) + + // THEN + val expectedDownEvent = InjectKeyEventModel( + + keyCode = 1, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = 11, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_KEYBOARD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) + } + + @Test + fun `perform key event action with no device name, ensure action is still performed with correct device id`() = + runTest(testDispatcher) { + // GIVEN + val descriptor = "fake_device_descriptor" + + val action = ActionData.InputKeyEvent( + keyCode = 1, + metaState = 0, + device = ActionData.InputKeyEvent.Device(descriptor = descriptor, name = ""), + ) + + fakeDevicesAdapter.connectedInputDevices.value = State.Data( + listOf( + InputDeviceInfo( + descriptor = descriptor, + name = "fake_name", + id = 10, + isExternal = true, + isGameController = false, + sources = InputDevice.SOURCE_GAMEPAD, + ), + ), + ) + + // WHEN + delegate.perform( + action, + inputEventAction = InputEventAction.DOWN_UP, + keyMetaState = 0, + triggerDevice = PerformActionTriggerDevice.Default, + ) + + // THEN + val expectedDownEvent = InjectKeyEventModel( + + keyCode = 1, + action = KeyEvent.ACTION_DOWN, + metaState = 0, + deviceId = 10, + scanCode = 0, + repeatCount = 0, + source = InputDevice.SOURCE_KEYBOARD, + ) + + val expectedUpEvent = expectedDownEvent.copy(action = KeyEvent.ACTION_UP) + + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedDownEvent, false) + verify(mockInputEventHub, times(1)).injectKeyEvent(expectedUpEvent, false) + } +} diff --git a/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt new file mode 100644 index 0000000000..dccbc31835 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/detection/KeyMapDetectionControllerTest.kt @@ -0,0 +1,1116 @@ +package io.github.sds100.keymapper.base.detection + +import android.view.KeyEvent +import io.github.sds100.keymapper.base.actions.Action +import io.github.sds100.keymapper.base.actions.ActionData +import io.github.sds100.keymapper.base.keymaps.ClickType +import io.github.sds100.keymapper.base.keymaps.KeyMap +import io.github.sds100.keymapper.base.trigger.EvdevTriggerKey +import io.github.sds100.keymapper.base.trigger.KeyEventTriggerDevice +import io.github.sds100.keymapper.base.trigger.KeyEventTriggerKey +import io.github.sds100.keymapper.base.trigger.Trigger +import io.github.sds100.keymapper.base.utils.parallelTrigger +import io.github.sds100.keymapper.base.utils.singleKeyTrigger +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.system.inputevents.Scancode +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.contains +import org.hamcrest.collection.IsEmptyCollection.empty +import org.junit.After +import org.junit.Before +import org.junit.Test +import org.mockito.MockedStatic +import org.mockito.Mockito.mockStatic +import org.mockito.kotlin.mock + +@OptIn(ExperimentalCoroutinesApi::class) +class KeyMapDetectionControllerTest { + + companion object { + private val FAKE_CONTROLLER_EVDEV_DEVICE = EvdevDeviceInfo( + name = "Fake Controller", + bus = 1, + vendor = 2, + product = 1, + ) + + private val FAKE_CONTROLLER_EVDEV_DEVICE_2 = EvdevDeviceInfo( + name = "Fake Controller 2", + bus = 1, + vendor = 3, + product = 2, + ) + } + + private val testDispatcher = UnconfinedTestDispatcher() + private val testScope = TestScope(testDispatcher) + private lateinit var algorithm: KeyMapAlgorithm + private lateinit var mockedKeyEvent: MockedStatic + + @Before + fun init() { + mockedKeyEvent = mockStatic(KeyEvent::class.java) + mockedKeyEvent.`when` { KeyEvent.getMaxKeyCode() }.thenReturn(1000) + + algorithm = KeyMapAlgorithm( + coroutineScope = testScope, + useCase = mock(), + performActionsUseCase = mock(), + detectConstraints = mock(), + ) + } + + @After + fun tearDown() { + mockedKeyEvent.close() + } + + @Test + fun `Only grab the same device once if multiple key maps add key events`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf( + KeyEvent.KEYCODE_BUTTON_X, + KeyEvent.KEYCODE_BUTTON_Y, + ), + ), + ), + ) + } + + @Test + fun `Grab multiple evdev devices from multiple triggers with key event actions`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_Y), + ), + ), + ) + } + + @Test + fun `Do not grab evdev devices with extra key codes if key event actions do not use system bridge`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests( + algorithm, + injectKeyEventActionsWithSystemBridge = false, + ) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Grab multiple evdev devices from multiple triggers`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Grab multiple evdev devices from the same trigger`() { + loadKeyMaps( + KeyMap( + trigger = parallelTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Grab evdev device for evdev trigger`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Do not grab evdev device if key map has no actions`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = emptyList(), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + assertThat(grabRequests, empty()) + } + + @Test + fun `Grab evdev device with extra key codes if action inputs key events`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_B)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_B), + ), + ), + ) + } + + @Test + fun `Do not grab evdev device for key event trigger`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + KeyEventTriggerKey( + keyCode = KeyEvent.KEYCODE_VOLUME_DOWN, + clickType = ClickType.SHORT_PRESS, + device = KeyEventTriggerDevice.Any, + ), + ), + ), + ) + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + assertThat(grabRequests, empty()) + } + + @Test + fun `do not grab any evdev devices if no triggers`() { + algorithm.loadKeyMaps(emptyList()) + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + assertThat(grabRequests, empty()) + } + + // ==================== TRIGGER KEY COMBINATION EDGE CASES ==================== + + @Test + fun `Same key on different devices in parallel trigger should grab both devices`() { + loadKeyMaps( + KeyMap( + trigger = parallelTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Long press trigger should grab device`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.LONG_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Double press trigger should grab device`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.DOUBLE_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Same key with different click types in different key maps should grab device once`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.LONG_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.GoHome)), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.DOUBLE_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.GoBack)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Mixed evdev and key event triggers in parallel should only grab evdev device`() { + loadKeyMaps( + KeyMap( + trigger = parallelTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + KeyEventTriggerKey( + keyCode = KeyEvent.KEYCODE_VOLUME_DOWN, + clickType = ClickType.SHORT_PRESS, + device = KeyEventTriggerDevice.Any, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + // ==================== ACTION EDGE CASES ==================== + + @Test + fun `Multiple key event actions should add all key codes to extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_L1), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf( + KeyEvent.KEYCODE_BUTTON_X, + KeyEvent.KEYCODE_BUTTON_Y, + KeyEvent.KEYCODE_BUTTON_L1, + ), + ), + ), + ) + } + + @Test + fun `Duplicate key event actions should not duplicate extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + ) + } + + @Test + fun `Action that inputs same key code as trigger key should include in extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_A), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_A), + ), + ), + ) + } + + @Test + fun `Key map with only non-key-event actions should have empty extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + Action(data = ActionData.GoHome), + Action(data = ActionData.Screenshot), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Key event action with meta state should include key code in extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action( + data = ActionData.InputKeyEvent( + keyCode = KeyEvent.KEYCODE_C, + metaState = KeyEvent.META_CTRL_ON, + ), + ), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_C), + ), + ), + ) + } + + @Test + fun `Mixed key event and non-key-event actions should only include key codes from key event actions`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf( + Action(data = ActionData.OpenCamera), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X), + Action(data = ActionData.GoHome), + buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y), + Action(data = ActionData.Screenshot), + ), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf( + KeyEvent.KEYCODE_BUTTON_X, + KeyEvent.KEYCODE_BUTTON_Y, + ), + ), + ), + ) + } + + // ==================== DEVICE HANDLING EDGE CASES ==================== + + @Test + fun `Disabled key map should not produce grab requests`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + isEnabled = false, + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat(grabRequests, empty()) + } + + @Test + fun `Mix of enabled and disabled key maps should only grab for enabled ones`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + isEnabled = true, + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf(Action(data = ActionData.GoHome)), + isEnabled = false, + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + @Test + fun `Disabled key map with key event actions should not contribute extra key codes`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X)), + isEnabled = true, + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_Y)), + isEnabled = false, + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + ) + } + + // ==================== EMPTY/NULL AND DUPLICATE HANDLING ==================== + + @Test + fun `Key map with empty trigger keys should not produce grab requests`() { + loadKeyMaps( + KeyMap( + trigger = Trigger(keys = emptyList()), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat(grabRequests, empty()) + } + + @Test + fun `Same extra key code from multiple key maps on same device should not duplicate`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X)), + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_BUTTON_X)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = intArrayOf(KeyEvent.KEYCODE_BUTTON_X), + ), + ), + ) + } + + @Test + fun `All key maps disabled should produce empty grab requests`() { + loadKeyMaps( + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + isEnabled = false, + ), + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE_2, + ), + ), + actionList = listOf(Action(data = ActionData.GoHome)), + isEnabled = false, + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat(grabRequests, empty()) + } + + @Test + fun `Large number of key maps should correctly aggregate grab requests`() { + val keyMaps = (0 until 10).map { index -> + KeyMap( + trigger = singleKeyTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A + index, + keyCode = KeyEvent.KEYCODE_BUTTON_A + index, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(buildKeyEventAction(KeyEvent.KEYCODE_0 + index)), + ) + } + loadKeyMaps(*keyMaps.toTypedArray()) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + val expectedExtraKeyCodes = (0 until 10).map { KeyEvent.KEYCODE_0 + it }.toIntArray() + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + device = FAKE_CONTROLLER_EVDEV_DEVICE, + extraKeyCodes = expectedExtraKeyCodes, + ), + ), + ) + } + + @Test + fun `Parallel trigger with multiple keys on same device should grab once`() { + loadKeyMaps( + KeyMap( + trigger = parallelTrigger( + EvdevTriggerKey( + scanCode = Scancode.BTN_A, + keyCode = KeyEvent.KEYCODE_BUTTON_A, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + EvdevTriggerKey( + scanCode = Scancode.BTN_B, + keyCode = KeyEvent.KEYCODE_BUTTON_B, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + EvdevTriggerKey( + scanCode = Scancode.BTN_X, + keyCode = KeyEvent.KEYCODE_BUTTON_X, + clickType = ClickType.SHORT_PRESS, + device = FAKE_CONTROLLER_EVDEV_DEVICE, + ), + ), + actionList = listOf(Action(data = ActionData.OpenCamera)), + ), + ) + + val grabRequests = KeyMapDetectionController.getEvdevGrabRequests(algorithm) + + assertThat( + grabRequests, + contains( + GrabTargetKeyCode( + name = FAKE_CONTROLLER_EVDEV_DEVICE.name, + bus = FAKE_CONTROLLER_EVDEV_DEVICE.bus, + vendor = FAKE_CONTROLLER_EVDEV_DEVICE.vendor, + product = FAKE_CONTROLLER_EVDEV_DEVICE.product, + extraKeyCodes = intArrayOf(), + ), + ), + ) + } + + private fun buildKeyEventAction(keyCode: Int): Action { + return Action( + data = ActionData.InputKeyEvent(keyCode = keyCode), + ) + } + + private fun loadKeyMaps(vararg keyMap: KeyMap) { + val models = listOf(*keyMap).map { DetectKeyMapModel(it) } + algorithm.loadKeyMaps(models) + } +} diff --git a/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt new file mode 100644 index 0000000000..7161d21886 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeAutoStarterTest.kt @@ -0,0 +1,704 @@ +package io.github.sds100.keymapper.base.expertmode + +import io.github.sds100.keymapper.base.R +import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository +import io.github.sds100.keymapper.base.utils.TestBuildConfigProvider +import io.github.sds100.keymapper.base.utils.TestScopeClock +import io.github.sds100.keymapper.base.utils.ui.ResourceProvider +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupController +import io.github.sds100.keymapper.system.network.NetworkAdapter +import io.github.sds100.keymapper.system.notifications.NotificationAdapter +import io.github.sds100.keymapper.system.notifications.NotificationModel +import io.github.sds100.keymapper.system.permissions.Permission +import io.github.sds100.keymapper.system.permissions.PermissionAdapter +import io.github.sds100.keymapper.system.root.SuAdapter +import io.github.sds100.keymapper.system.shizuku.ShizukuAdapter +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.runBlocking +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.UnconfinedTestDispatcher +import kotlinx.coroutines.test.advanceTimeBy +import kotlinx.coroutines.test.advanceUntilIdle +import kotlinx.coroutines.test.runTest +import org.hamcrest.CoreMatchers.`is` +import org.hamcrest.MatcherAssert.assertThat +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.any +import org.mockito.kotlin.argumentCaptor +import org.mockito.kotlin.atLeast +import org.mockito.kotlin.doReturn +import org.mockito.kotlin.inOrder +import org.mockito.kotlin.mock +import org.mockito.kotlin.never +import org.mockito.kotlin.verify +import org.mockito.kotlin.whenever + +@ExperimentalCoroutinesApi +@RunWith(MockitoJUnitRunner::class) +class SystemBridgeAutoStarterTest { + + private val testDispatcher = UnconfinedTestDispatcher() + private val testCoroutineScope = TestScope(testDispatcher) + + private lateinit var systemBridgeAutoStarter: SystemBridgeAutoStarter + + private lateinit var testScopeClock: TestScopeClock + private lateinit var mockSuAdapter: SuAdapter + private lateinit var mockShizukuAdapter: ShizukuAdapter + private lateinit var mockConnectionManager: SystemBridgeConnectionManager + private lateinit var mockSetupController: SystemBridgeSetupController + private lateinit var fakePreferences: FakePreferenceRepository + private lateinit var mockNetworkAdapter: NetworkAdapter + private lateinit var mockPermissionAdapter: PermissionAdapter + private lateinit var mockNotificationAdapter: NotificationAdapter + private lateinit var mockResourceProvider: ResourceProvider + private lateinit var testBuildConfig: TestBuildConfigProvider + + private lateinit var isRootGrantedFlow: MutableStateFlow + private lateinit var shizukuIsStartedFlow: MutableStateFlow + private lateinit var connectionStateFlow: MutableStateFlow + private lateinit var isWifiConnectedFlow: MutableStateFlow + private lateinit var writeSecureSettingsGrantedFlow: MutableStateFlow + private lateinit var shizukuPermissionGrantedFlow: MutableStateFlow + + @Before + fun init() { + isRootGrantedFlow = MutableStateFlow(false) + shizukuIsStartedFlow = MutableStateFlow(false) + connectionStateFlow = MutableStateFlow( + SystemBridgeConnectionState.Disconnected( + time = testCoroutineScope.testScheduler.currentTime, + isStoppedByUser = false, + ), + ) + isWifiConnectedFlow = MutableStateFlow(false) + writeSecureSettingsGrantedFlow = MutableStateFlow(false) + shizukuPermissionGrantedFlow = MutableStateFlow(false) + + mockSuAdapter = mock { + on { isRootGranted } doReturn isRootGrantedFlow + } + + mockShizukuAdapter = mock { + on { isStarted } doReturn shizukuIsStartedFlow + } + + mockConnectionManager = mock { + on { connectionState } doReturn connectionStateFlow + } + + mockSetupController = mock() + runBlocking { + whenever(mockSetupController.isAdbPaired()).doReturn(false) + } + + fakePreferences = FakePreferenceRepository() + + mockNetworkAdapter = mock { + on { isWifiConnected } doReturn isWifiConnectedFlow + } + + mockPermissionAdapter = mock { + on { isGrantedFlow(Permission.WRITE_SECURE_SETTINGS) } doReturn + writeSecureSettingsGrantedFlow + on { isGrantedFlow(Permission.SHIZUKU) } doReturn shizukuPermissionGrantedFlow + } + + mockNotificationAdapter = mock() + + mockResourceProvider = mock { + on { getString(any()) } doReturn "test_string" + } + + testScopeClock = TestScopeClock(testCoroutineScope) + + testBuildConfig = TestBuildConfigProvider(sdkInt = 30) + + systemBridgeAutoStarter = SystemBridgeAutoStarter( + coroutineScope = testCoroutineScope, + clock = testScopeClock, + suAdapter = mockSuAdapter, + shizukuAdapter = mockShizukuAdapter, + connectionManager = mockConnectionManager, + setupController = mockSetupController, + preferences = fakePreferences, + networkAdapter = mockNetworkAdapter, + permissionAdapter = mockPermissionAdapter, + notificationAdapter = mockNotificationAdapter, + resourceProvider = mockResourceProvider, + buildConfig = testBuildConfig, + ) + } + + @Test + fun `auto start within 60 seconds of booting`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + isWifiConnectedFlow.value = true + writeSecureSettingsGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockSetupController).autoStartWithAdb() + } + + @Test + fun `auto start 5 minutes after booting`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + isWifiConnectedFlow.value = true + writeSecureSettingsGrantedFlow.value = true + + advanceTimeBy(310_000) + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockSetupController).autoStartWithAdb() + } + + @Test + fun `do not auto start with ADB if WRITE_SECURE_SETTINGS is denied`() = + runTest(testDispatcher) { + advanceTimeBy(5000L) + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + + writeSecureSettingsGrantedFlow.value = false + isWifiConnectedFlow.value = false + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `do not auto start with ADB if wifi is disconnected`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + writeSecureSettingsGrantedFlow.value = true + isWifiConnectedFlow.value = false + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `auto start with shizuku on Android 10`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + testBuildConfig.sdkInt = 29 + isRootGrantedFlow.value = false + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithShizuku() + } + + @Test + fun `auto start with root on Android 9`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + testBuildConfig.sdkInt = 28 + isRootGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithRoot() + } + + @Test + fun `auto start from pre version 4 when rooted and it was never used before`() = + runTest(testDispatcher) { + isRootGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeUsed, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithRoot() + } + + @Test + fun `auto start from pre version 4 when shizuku is started and it was never used before`() = + runTest(testDispatcher) { + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeUsed, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithShizuku() + } + + @Test + fun `set key event actions to use system bridge when upgrading to version 4 and rooted`() = + runTest(testDispatcher) { + isRootGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeUsed, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + assertThat(fakePreferences.get(Keys.keyEventActionsUseSystemBridge).first(), `is`(true)) + } + + @Test + fun `set key event actions to use system bridge when upgrading to version 4 and shizuku is started`() = + runTest(testDispatcher) { + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeUsed, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, null) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + assertThat(fakePreferences.get(Keys.keyEventActionsUseSystemBridge).first(), `is`(true)) + } + + @Test + fun `prioritize auto starting with root if shizuku also started`() = runTest(testDispatcher) { + advanceTimeBy(1_000_000L) + isRootGrantedFlow.value = true + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithRoot() + } + + @Test + fun `do not auto start from pre version 4 when shizuku permission granted but not started`() = + runTest(testDispatcher) { + advanceTimeBy(1_000_000L) + shizukuIsStartedFlow.value = false + shizukuPermissionGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithShizuku() + } + + @Test + fun `auto start with root`() = runTest(testDispatcher) { + advanceTimeBy(1_000_000L) + isRootGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeEmergencyKilled, false) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithRoot() + } + + @Test + fun `do not auto start when emergency killed`() = runTest(testDispatcher) { + isRootGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeEmergencyKilled, true) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `auto start with shizuku when shizuku is available`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager).startWithShizuku() + } + + @Test + fun `auto start with Shizuku if Shizuku takes a minute to connect`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + // Initially Shizuku is disconnected, and is connected 50 seconds after booting + shizukuIsStartedFlow.value = false + shizukuPermissionGrantedFlow.value = true + + inOrder(mockConnectionManager) { + systemBridgeAutoStarter.init() + advanceTimeBy(50000) + verify(mockConnectionManager, never()).startWithShizuku() + + // Shizuku starts + shizukuIsStartedFlow.value = true + + advanceTimeBy(20000) + verify(mockConnectionManager).startWithShizuku() + } + } + + @Test + fun `auto start with ADB if WiFi takes a minute to connect`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + writeSecureSettingsGrantedFlow.value = true + + // Initially the wifi is disconnected, and is connected 50 seconds after booting + isWifiConnectedFlow.value = false + + inOrder(mockSetupController) { + systemBridgeAutoStarter.init() + advanceTimeBy(50000) + verify(mockSetupController, never()).autoStartWithAdb() + + // Connect to Wi-Fi network + isWifiConnectedFlow.value = true + + advanceTimeBy(20000) + verify(mockSetupController).autoStartWithAdb() + } + } + + @Test + fun `do not auto start when auto start is disabled`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, false) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) + isRootGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `do not auto start when already connected`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + systemBridgeAutoStarter.init() + advanceTimeBy(5000) + + // The system bridge connects during init() + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 1000) + + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `show notification when auto starting`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + isWifiConnectedFlow.value = true + writeSecureSettingsGrantedFlow.value = true + + inOrder(mockNotificationAdapter) { + systemBridgeAutoStarter.init() + advanceTimeBy(10000) + + // Show the notification that it is auto starting + verify(mockNotificationAdapter).showNotification(any()) + + // Set the state as connected within the timeout + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 10000) + advanceUntilIdle() + // Do not show another notification after the timeout + verify(mockNotificationAdapter, never()).showNotification(any()) + } + } + + @Test + fun `show failed notification when connection times out`() = runTest(testDispatcher) { + isRootGrantedFlow.value = true + fakePreferences.set(Keys.isSystemBridgeEmergencyKilled, false) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 0, isStoppedByUser = false) + + systemBridgeAutoStarter.init() + // The system bridge remains disconnected + advanceUntilIdle() + + verify(mockConnectionManager).startWithRoot() + verify(mockNotificationAdapter, atLeast(1)).showNotification(any()) + } + + @Test + fun `do not auto start when connected`() = runTest(testDispatcher) { + advanceTimeBy(1_000_000L) + val connectedState = SystemBridgeConnectionState.Connected(time = 1000) + connectionStateFlow.value = connectedState + isRootGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `do not auto restart when disconnected by the user`() = runTest(testDispatcher) { + advanceTimeBy(5000L) + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + isRootGrantedFlow.value = true + + inOrder(mockConnectionManager, mockSetupController) { + systemBridgeAutoStarter.init() + advanceTimeBy(100_000) + verify(mockConnectionManager).startWithRoot() + + // Disconnect the system bridge. Expected is true + connectionStateFlow.value = SystemBridgeConnectionState.Disconnected( + time = 1_000_000L, + isStoppedByUser = true, + ) + + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + } + + @Test + fun `do not auto restart within 5 minutes of the last auto start`() = runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + isRootGrantedFlow.value = true + + inOrder(mockConnectionManager) { + systemBridgeAutoStarter.init() + advanceTimeBy(6000) + // Try auto starting when the process launches + verify(mockConnectionManager).startWithRoot() + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 6000) + + advanceUntilIdle() + // It is killed unexpectedly straight after auto starting + connectionStateFlow.value = SystemBridgeConnectionState.Disconnected( + time = 7000, + isStoppedByUser = true, + ) + + advanceUntilIdle() + // Auto starting fails and so it does not try starting again + verify(mockConnectionManager, never()).startWithRoot() + } + } + + @Test + fun `show killed and not restarting notification if want to autostart again within the cooldown`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + whenever( + mockResourceProvider.getString(R.string.system_bridge_died_notification_title), + ).thenReturn("died") + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + isWifiConnectedFlow.value = true + writeSecureSettingsGrantedFlow.value = true + + inOrder(mockNotificationAdapter) { + systemBridgeAutoStarter.init() + advanceTimeBy(10000) + + // Show the notification that it is auto starting + verify(mockNotificationAdapter).showNotification(any()) + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 10000) + + // Set the state as disconnected within the timeout + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 11000, isStoppedByUser = false) + advanceUntilIdle() + + // Show notification + val argument = argumentCaptor() + verify(mockNotificationAdapter).showNotification(argument.capture()) + + assertThat(argument.firstValue.title, `is`("died")) + } + } + + @Test + fun `auto restart when shizuku connects after the system bridge dies`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + // Shizuku is not started initially + shizukuIsStartedFlow.value = false + shizukuPermissionGrantedFlow.value = true + + // System bridge was already running when Key Mapper process launched + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 0) + + inOrder(mockConnectionManager) { + systemBridgeAutoStarter.init() + advanceUntilIdle() + + // If it dies, then it will wait for shizuku to connect + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 3000, isStoppedByUser = false) + + advanceTimeBy(2000) + verify(mockConnectionManager, never()).startWithShizuku() + + shizukuIsStartedFlow.value = true + advanceUntilIdle() + + verify(mockConnectionManager).startWithShizuku() + } + } + + @Test + fun `do not auto start when shizuku is connected if system bridge was stopped by the user`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + // Shizuku is not started initially + shizukuIsStartedFlow.value = false + shizukuPermissionGrantedFlow.value = true + + // System bridge was already running when Key Mapper process launched + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 0) + + inOrder(mockConnectionManager) { + systemBridgeAutoStarter.init() + advanceUntilIdle() + + // The user stopped the system bridge + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 3000, isStoppedByUser = true) + + advanceTimeBy(2000) + verify(mockConnectionManager, never()).startWithShizuku() + + shizukuIsStartedFlow.value = true + advanceUntilIdle() + + // Do not start it when shizuku connects + verify(mockConnectionManager, never()).startWithShizuku() + } + } + + @Test + fun `auto restart when wifi is connected after the system bridge dies`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + fakePreferences.set(Keys.isSystemBridgeUsed, true) + + // WiFi is not connected initially + isWifiConnectedFlow.value = false + writeSecureSettingsGrantedFlow.value = true + whenever(mockSetupController.isAdbPaired()).thenReturn(true) + + // System bridge was already running when Key Mapper process launched + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 0) + + inOrder(mockSetupController) { + systemBridgeAutoStarter.init() + advanceUntilIdle() + + // If it dies, then it will wait for wifi to connect + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 2000, isStoppedByUser = false) + + advanceTimeBy(2000) + verify(mockSetupController, never()).autoStartWithAdb() + + isWifiConnectedFlow.value = true + advanceUntilIdle() + + verify(mockSetupController).autoStartWithAdb() + } + } + + @Test + fun `do not auto start with Shizuku on launch if it was never used before`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeUsed, null) + fakePreferences.set(Keys.handledUpgradeToExpertMode, true) + shizukuIsStartedFlow.value = true + shizukuPermissionGrantedFlow.value = true + + systemBridgeAutoStarter.init() + advanceUntilIdle() + + verify(mockConnectionManager, never()).startWithRoot() + verify(mockConnectionManager, never()).startWithShizuku() + verify(mockSetupController, never()).autoStartWithAdb() + } + + @Test + fun `do not auto restart if keep alive is disabled while it is connected`() = + runTest(testDispatcher) { + fakePreferences.set(Keys.isSystemBridgeUsed, true) + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, true) + isRootGrantedFlow.value = true + + inOrder(mockConnectionManager) { + systemBridgeAutoStarter.init() + advanceTimeBy(6000) + + connectionStateFlow.value = SystemBridgeConnectionState.Connected(time = 6000) + verify(mockConnectionManager).startWithRoot() + + // The user disabled keep alive while system bridge is running + fakePreferences.set(Keys.isSystemBridgeKeepAliveEnabled, false) + // The system bridge dies + connectionStateFlow.value = + SystemBridgeConnectionState.Disconnected(time = 7000, isStoppedByUser = false) + + advanceTimeBy(2000) + + // The system bridge should not be auto restarted. + verify(mockConnectionManager, never()).startWithRoot() + } + } +} diff --git a/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt new file mode 100644 index 0000000000..db43edbbc6 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/expertmode/SystemBridgeSetupUseCaseTest.kt @@ -0,0 +1,131 @@ +package io.github.sds100.keymapper.base.expertmode + +import io.github.sds100.keymapper.base.repositories.FakePreferenceRepository +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager +import io.github.sds100.keymapper.sysbridge.service.SystemBridgeSetupController +import io.github.sds100.keymapper.system.accessibility.AccessibilityServiceAdapter +import io.github.sds100.keymapper.system.network.NetworkAdapter +import io.github.sds100.keymapper.system.permissions.PermissionAdapter +import io.github.sds100.keymapper.system.root.SuAdapter +import io.github.sds100.keymapper.system.shizuku.ShizukuAdapter +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.flow.first +import kotlinx.coroutines.test.runTest +import org.hamcrest.MatcherAssert.assertThat +import org.hamcrest.Matchers.`is` +import org.junit.Before +import org.junit.Test +import org.junit.runner.RunWith +import org.mockito.junit.MockitoJUnitRunner +import org.mockito.kotlin.mock +import org.mockito.kotlin.verify + +@ExperimentalCoroutinesApi +@RunWith(MockitoJUnitRunner::class) +class SystemBridgeSetupUseCaseTest { + + private lateinit var useCase: SystemBridgeSetupUseCaseImpl + private lateinit var fakePreferences: FakePreferenceRepository + private lateinit var mockSuAdapter: SuAdapter + private lateinit var mockSystemBridgeSetupController: SystemBridgeSetupController + private lateinit var mockSystemBridgeConnectionManager: SystemBridgeConnectionManager + private lateinit var mockShizukuAdapter: ShizukuAdapter + private lateinit var mockPermissionAdapter: PermissionAdapter + private lateinit var mockAccessibilityServiceAdapter: AccessibilityServiceAdapter + private lateinit var mockNetworkAdapter: NetworkAdapter + + @Before + fun init() { + fakePreferences = FakePreferenceRepository() + mockSuAdapter = mock() + mockSystemBridgeSetupController = mock() + mockSystemBridgeConnectionManager = mock() + mockShizukuAdapter = mock() + mockPermissionAdapter = mock() + mockAccessibilityServiceAdapter = mock() + mockNetworkAdapter = mock() + + useCase = SystemBridgeSetupUseCaseImpl( + preferences = fakePreferences, + suAdapter = mockSuAdapter, + systemBridgeSetupController = mockSystemBridgeSetupController, + systemBridgeConnectionManager = mockSystemBridgeConnectionManager, + shizukuAdapter = mockShizukuAdapter, + permissionAdapter = mockPermissionAdapter, + accessibilityServiceAdapter = mockAccessibilityServiceAdapter, + networkAdapter = mockNetworkAdapter, + ) + } + + @Test + fun `set isSystemBridgeStoppedByUser to true when stopping system bridge`() = runTest { + useCase.stopSystemBridge() + + assertThat( + fakePreferences.get(Keys.isSystemBridgeStoppedByUser).first(), + `is`(true), + ) + verify(mockSystemBridgeConnectionManager).stopSystemBridge() + } + + @Test + fun `set isSystemBridgeEmergencyKilled and isSystemBridgeStoppedByUser to false when starting system bridge with root`() = + runTest { + useCase.startSystemBridgeWithRoot() + + assertThat( + fakePreferences.get(Keys.isSystemBridgeEmergencyKilled).first(), + `is`(false), + ) + assertThat( + fakePreferences.get(Keys.isSystemBridgeStoppedByUser).first(), + `is`(false), + ) + } + + @Test + fun `set isSystemBridgeEmergencyKilled and isSystemBridgeStoppedByUser to false when starting system bridge with shizuku`() = + runTest { + useCase.startSystemBridgeWithShizuku() + + assertThat( + fakePreferences.get(Keys.isSystemBridgeEmergencyKilled).first(), + `is`(false), + ) + assertThat( + fakePreferences.get(Keys.isSystemBridgeStoppedByUser).first(), + `is`(false), + ) + } + + @Test + fun `set isSystemBridgeEmergencyKilled and isSystemBridgeStoppedByUser to false when starting system bridge with adb`() = + runTest { + useCase.startSystemBridgeWithAdb() + + assertThat( + fakePreferences.get(Keys.isSystemBridgeEmergencyKilled).first(), + `is`(false), + ) + assertThat( + fakePreferences.get(Keys.isSystemBridgeStoppedByUser).first(), + `is`(false), + ) + } + + @Test + fun `set isSystemBridgeEmergencyKilled and isSystemBridgeStoppedByUser to false when auto starting system bridge with adb`() = + runTest { + useCase.startSystemBridgeWithAdb() + + assertThat( + fakePreferences.get(Keys.isSystemBridgeEmergencyKilled).first(), + `is`(false), + ) + assertThat( + fakePreferences.get(Keys.isSystemBridgeStoppedByUser).first(), + `is`(false), + ) + } +} diff --git a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/ProcessKeyMapGroupsForDetectionTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DetectKeyMapsUseCaseTest.kt similarity index 99% rename from base/src/test/java/io/github/sds100/keymapper/base/keymaps/ProcessKeyMapGroupsForDetectionTest.kt rename to base/src/test/java/io/github/sds100/keymapper/base/keymaps/DetectKeyMapsUseCaseTest.kt index c20fc2682c..a5f20562cf 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/ProcessKeyMapGroupsForDetectionTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DetectKeyMapsUseCaseTest.kt @@ -11,7 +11,7 @@ import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers import org.junit.Test -class ProcessKeyMapGroupsForDetectionTest { +class DetectKeyMapsUseCaseTest { @Test fun `Key map in grandchild group, all have constraints, and parent does not exist then ignore key map`() { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DpadMotionEventTrackerTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DpadMotionEventTrackerTest.kt index 0cf0ab5c52..d920f854f7 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DpadMotionEventTrackerTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/DpadMotionEventTrackerTest.kt @@ -6,7 +6,6 @@ import io.github.sds100.keymapper.base.detection.DpadMotionEventTracker import io.github.sds100.keymapper.common.utils.InputDeviceInfo import io.github.sds100.keymapper.system.inputevents.KMGamePadEvent import io.github.sds100.keymapper.system.inputevents.KMKeyEvent -import junitparams.JUnitParamsRunner import kotlinx.coroutines.ExperimentalCoroutinesApi import org.hamcrest.MatcherAssert.assertThat import org.hamcrest.Matchers.hasItem @@ -14,10 +13,8 @@ import org.hamcrest.Matchers.hasSize import org.hamcrest.Matchers.`is` import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith @ExperimentalCoroutinesApi -@RunWith(JUnitParamsRunner::class) class DpadMotionEventTrackerTest { companion object { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt index 11a00e269a..8a066f8581 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/KeyMapAlgorithmTest.kt @@ -6,6 +6,7 @@ import androidx.arch.core.executor.testing.InstantTaskExecutorRule import io.github.sds100.keymapper.base.actions.Action import io.github.sds100.keymapper.base.actions.ActionData import io.github.sds100.keymapper.base.actions.ActionErrorSnapshot +import io.github.sds100.keymapper.base.actions.PerformActionTriggerDevice import io.github.sds100.keymapper.base.actions.PerformActionsUseCase import io.github.sds100.keymapper.base.actions.RepeatMode import io.github.sds100.keymapper.base.constraints.Constraint @@ -30,7 +31,6 @@ import io.github.sds100.keymapper.base.utils.parallelTrigger import io.github.sds100.keymapper.base.utils.sequenceTrigger import io.github.sds100.keymapper.base.utils.singleKeyTrigger import io.github.sds100.keymapper.base.utils.triggerKey -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle import io.github.sds100.keymapper.common.models.EvdevDeviceInfo import io.github.sds100.keymapper.common.utils.InputDeviceInfo import io.github.sds100.keymapper.common.utils.InputEventAction @@ -253,7 +253,10 @@ class KeyMapAlgorithmTest { ) inputUpEvdevEvent(KeyEvent.KEYCODE_UNKNOWN, Scancode.BTN_LEFT, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -271,7 +274,13 @@ class KeyMapAlgorithmTest { inputDownEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify( + performActionsUseCase, + times(1), + ).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -286,10 +295,23 @@ class KeyMapAlgorithmTest { ) loadKeyMaps(KeyMap(trigger = trigger, actionList = listOf(TEST_ACTION))) - inputDownEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - inputUpEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) + inputDownEvdevEvent( + KeyEvent.KEYCODE_B, + Scancode.KEY_B, + FAKE_CONTROLLER_EVDEV_DEVICE, + deviceId = 1, + ) + inputUpEvdevEvent( + KeyEvent.KEYCODE_B, + Scancode.KEY_B, + FAKE_CONTROLLER_EVDEV_DEVICE, + deviceId = 1, + ) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 1), + ) } @Test @@ -309,7 +331,10 @@ class KeyMapAlgorithmTest { inputDownEvdevEvent(KeyEvent.KEYCODE_C, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_C, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -360,7 +385,10 @@ class KeyMapAlgorithmTest { inputDownEvdevEvent(KeyEvent.KEYCODE_Y, Scancode.KEY_D, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_Y, Scancode.KEY_D, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -392,7 +420,10 @@ class KeyMapAlgorithmTest { inputUpEvdevEvent(KeyEvent.KEYCODE_X, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_Y, Scancode.KEY_D, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -413,7 +444,10 @@ class KeyMapAlgorithmTest { delay(LONG_PRESS_DELAY + 100L) inputUpEvdevEvent(KeyEvent.KEYCODE_X, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -440,7 +474,10 @@ class KeyMapAlgorithmTest { delay(50L) inputUpEvdevEvent(KeyEvent.KEYCODE_X, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -546,7 +583,10 @@ class KeyMapAlgorithmTest { inputDownEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -572,7 +612,10 @@ class KeyMapAlgorithmTest { inputUpEvdevEvent(KeyEvent.KEYCODE_A, Scancode.KEY_A, FAKE_CONTROLLER_EVDEV_DEVICE) inputUpEvdevEvent(KeyEvent.KEYCODE_B, Scancode.KEY_B, FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -607,7 +650,10 @@ class KeyMapAlgorithmTest { mockEvdevKeyInput(trigger.keys[0], FAKE_CONTROLLER_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -624,7 +670,10 @@ class KeyMapAlgorithmTest { mockEvdevKeyInput(trigger.keys[0], FAKE_VOLUME_EVDEV_DEVICE) - verify(performActionsUseCase, times(1)).perform(TEST_ACTION.data) + verify(performActionsUseCase, times(1)).perform( + TEST_ACTION.data, + device = PerformActionTriggerDevice.Evdev(deviceId = 0), + ) } @Test @@ -1691,23 +1740,23 @@ class KeyMapAlgorithmTest { } } - whenever(performActionsUseCase.perform(any(), any(), any())).doAnswer { + whenever(performActionsUseCase.perform(any(), any(), any(), any())).doAnswer { isFlashlightEnabled = !isFlashlightEnabled } inOrder(performActionsUseCase) { // flashlight is initially disabled so don't trigger. mockTriggerKeyInput(keyMap.trigger.keys[0]) - verify(performActionsUseCase, never()).perform(any(), any(), any()) + verify(performActionsUseCase, never()).perform(any(), any(), any(), any()) isFlashlightEnabled = true // trigger because flashlight is enabled. Triggering the action will disable the flashlight. mockTriggerKeyInput(keyMap.trigger.keys[0]) - verify(performActionsUseCase, times(1)).perform(any(), any(), any()) + verify(performActionsUseCase, times(1)).perform(any(), any(), any(), any()) // Don't trigger because the flashlight is now disabled mockTriggerKeyInput(keyMap.trigger.keys[0]) - verify(performActionsUseCase, never()).perform(any(), any(), any()) + verify(performActionsUseCase, never()).perform(any(), any(), any(), any()) } } @@ -2839,7 +2888,7 @@ class KeyMapAlgorithmTest { mockTriggerKeyInput(triggerKey(KeyEvent.KEYCODE_A, clickType = ClickType.DOUBLE_PRESS)) - verify(performActionsUseCase, never()).perform(any(), any(), any()) + verify(performActionsUseCase, never()).perform(any(), any(), any(), any()) mockTriggerKeyInput(triggerKey(KeyEvent.KEYCODE_A)) mockTriggerKeyInput(triggerKey(KeyEvent.KEYCODE_A, clickType = ClickType.DOUBLE_PRESS)) @@ -3096,7 +3145,7 @@ class KeyMapAlgorithmTest { mockTriggerKeyInput(triggerKey(KeyEvent.KEYCODE_A, FAKE_KEYBOARD_TRIGGER_KEY_DEVICE)) // THEN - verify(performActionsUseCase, never()).perform(any(), any(), any()) + verify(performActionsUseCase, never()).perform(any(), any(), any(), any()) } @Test @@ -4746,43 +4795,51 @@ class KeyMapAlgorithmTest { ), ) - private fun inputDownEvdevEvent(keyCode: Int, scanCode: Int, device: EvdevDeviceInfo): Boolean = - controller.onInputEvent( - KMEvdevEvent( - type = KMEvdevEvent.TYPE_KEY_EVENT, - device = EvdevDeviceHandle( - path = "/dev/input${device.name}", - name = device.name, - bus = device.bus, - vendor = device.vendor, - product = device.product, - ), - code = scanCode, - androidCode = keyCode, - value = KMEvdevEvent.VALUE_DOWN, - timeSec = testScope.currentTime, - timeUsec = 0, + private fun inputDownEvdevEvent( + keyCode: Int, + scanCode: Int, + device: EvdevDeviceInfo, + deviceId: Int = 0, + ): Boolean = controller.onInputEvent( + KMEvdevEvent( + type = KMEvdevEvent.TYPE_KEY_EVENT, + deviceId = deviceId, + deviceInfo = EvdevDeviceInfo( + name = device.name, + bus = device.bus, + vendor = device.vendor, + product = device.product, ), - ) + code = scanCode, + androidCode = keyCode, + value = KMEvdevEvent.VALUE_DOWN, + timeSec = testScope.currentTime, + timeUsec = 0, + ), + ) - private fun inputUpEvdevEvent(keyCode: Int, scanCode: Int, device: EvdevDeviceInfo): Boolean = - controller.onInputEvent( - KMEvdevEvent( - type = KMEvdevEvent.TYPE_KEY_EVENT, - device = EvdevDeviceHandle( - path = "/dev/input${device.name}", - name = device.name, - bus = device.bus, - vendor = device.vendor, - product = device.product, - ), - code = scanCode, - androidCode = keyCode, - value = KMEvdevEvent.VALUE_UP, - timeSec = testScope.currentTime, - timeUsec = 0, + private fun inputUpEvdevEvent( + keyCode: Int, + scanCode: Int, + device: EvdevDeviceInfo, + deviceId: Int = 0, + ): Boolean = controller.onInputEvent( + KMEvdevEvent( + type = KMEvdevEvent.TYPE_KEY_EVENT, + deviceId = deviceId, + deviceInfo = EvdevDeviceInfo( + name = device.name, + bus = device.bus, + vendor = device.vendor, + product = device.product, ), - ) + code = scanCode, + androidCode = keyCode, + value = KMEvdevEvent.VALUE_UP, + timeSec = testScope.currentTime, + timeUsec = 0, + ), + ) private suspend fun mockParallelTrigger(trigger: Trigger, delay: Long? = null) { require(trigger.mode is TriggerMode.Parallel) diff --git a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/TriggerKeyMapFromOtherAppsControllerTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/TriggerKeyMapFromOtherAppsControllerTest.kt index 8383bbc63f..4bc6370c7e 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/keymaps/TriggerKeyMapFromOtherAppsControllerTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/keymaps/TriggerKeyMapFromOtherAppsControllerTest.kt @@ -11,7 +11,6 @@ import io.github.sds100.keymapper.base.detection.TriggerKeyMapFromOtherAppsContr import io.github.sds100.keymapper.base.trigger.Trigger import io.github.sds100.keymapper.base.utils.TestConstraintSnapshot import io.github.sds100.keymapper.common.utils.KMError -import junitparams.JUnitParamsRunner import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.delay import kotlinx.coroutines.flow.MutableStateFlow @@ -21,14 +20,12 @@ import kotlinx.coroutines.test.advanceUntilIdle import kotlinx.coroutines.test.runTest import org.junit.Before import org.junit.Test -import org.junit.runner.RunWith import org.mockito.kotlin.doReturn import org.mockito.kotlin.mock import org.mockito.kotlin.times import org.mockito.kotlin.verify @ExperimentalCoroutinesApi -@RunWith(JUnitParamsRunner::class) class TriggerKeyMapFromOtherAppsControllerTest { companion object { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt b/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt index cf6696a678..1ed7f2bc56 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/trigger/ConfigTriggerViewModelTest.kt @@ -9,9 +9,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible when system bridge is connected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -20,12 +20,12 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when system bridge is disconnected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, - isExpected = true, + isStoppedByUser = true, ), ) @@ -34,12 +34,12 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when system bridge is disconnected unexpectedly`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, - isExpected = false, + isStoppedByUser = false, ), ) @@ -48,9 +48,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is checked when pro mode recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -59,9 +59,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not checked when pro mode recording is disabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -70,9 +70,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is enabled when record trigger state is idle`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -81,9 +81,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is enabled when record trigger state is completed`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Completed(emptyList()), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -92,9 +92,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is disabled when record trigger state is counting down`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 3), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -103,9 +103,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is disabled when counting down even if pro mode recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 5), - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -115,9 +115,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible and checked when connected and enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -128,12 +128,12 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible when disconnected even if recording is enabled`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.Idle, - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, - isExpected = true, + isStoppedByUser = true, ), ) @@ -144,9 +144,9 @@ class ConfigTriggerViewModelTest { @Test fun `switch is visible but disabled when counting down and connected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 1), - isProModeRecordingEnabled = true, + isExpertModeRecordingEnabled = true, systemBridgeState = SystemBridgeConnectionState.Connected(time = 0L), ) @@ -157,12 +157,12 @@ class ConfigTriggerViewModelTest { @Test fun `switch is not visible and disabled when counting down and disconnected`() { - val result = BaseConfigTriggerViewModel.buildProModeSwitchState( + val result = BaseConfigTriggerViewModel.buildExpertModeSwitchState( recordTriggerState = RecordTriggerState.CountingDown(timeLeft = 2), - isProModeRecordingEnabled = false, + isExpertModeRecordingEnabled = false, systemBridgeState = SystemBridgeConnectionState.Disconnected( time = 0L, - isExpected = false, + isStoppedByUser = false, ), ) diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestBuildConfigProvider.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestBuildConfigProvider.kt index 67393a19b2..877cb35bd3 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestBuildConfigProvider.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestBuildConfigProvider.kt @@ -4,8 +4,8 @@ import android.os.Build import io.github.sds100.keymapper.base.BuildConfig import io.github.sds100.keymapper.common.BuildConfigProvider -class TestBuildConfigProvider(override val sdkInt: Int) : BuildConfigProvider { - override val minApi: Int = Build.VERSION_CODES.LOLLIPOP +class TestBuildConfigProvider(override var sdkInt: Int) : BuildConfigProvider { + override val minApi: Int = Build.VERSION_CODES.O override val maxApi: Int = 1000 override val packageName: String = BuildConfig.LIBRARY_PACKAGE_NAME override val version: String = "1.0.0" diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt index 063557172f..be77315302 100644 --- a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestConstraintSnapshot.kt @@ -4,6 +4,7 @@ import io.github.sds100.keymapper.base.constraints.Constraint import io.github.sds100.keymapper.base.constraints.ConstraintData import io.github.sds100.keymapper.base.constraints.ConstraintSnapshot import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.system.bluetooth.BluetoothDeviceInfo import io.github.sds100.keymapper.system.camera.CameraLens import io.github.sds100.keymapper.system.foldable.HingeState @@ -17,6 +18,7 @@ class TestConstraintSnapshot( val appInForeground: String? = null, val connectedBluetoothDevices: Set = emptySet(), val orientation: Orientation = Orientation.ORIENTATION_0, + val physicalOrientation: PhysicalOrientation = PhysicalOrientation.PORTRAIT, val isScreenOn: Boolean = false, val appsPlayingMedia: List = emptyList(), val isWifiEnabled: Boolean = false, @@ -62,6 +64,9 @@ class TestConstraintSnapshot( orientation == Orientation.ORIENTATION_0 || orientation == Orientation.ORIENTATION_180 + is ConstraintData.PhysicalOrientation -> + physicalOrientation == data.physicalOrientation + is ConstraintData.ScreenOff -> !isScreenOn is ConstraintData.ScreenOn -> isScreenOn is ConstraintData.FlashlightOff -> when (data.lens) { diff --git a/base/src/test/java/io/github/sds100/keymapper/base/utils/TestScopeClock.kt b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestScopeClock.kt new file mode 100644 index 0000000000..5cb9711011 --- /dev/null +++ b/base/src/test/java/io/github/sds100/keymapper/base/utils/TestScopeClock.kt @@ -0,0 +1,13 @@ +package io.github.sds100.keymapper.base.utils + +import io.github.sds100.keymapper.common.utils.Clock +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.TestScope +import kotlinx.coroutines.test.currentTime + +class TestScopeClock(private val testScope: TestScope) : Clock { + @OptIn(ExperimentalCoroutinesApi::class) + override fun elapsedRealtime(): Long { + return testScope.currentTime + } +} diff --git a/build.gradle.kts b/build.gradle.kts index 9e070cd106..78ed67584b 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -1,7 +1,9 @@ - // Top-level build file where you can add configuration options common to all sub-projects/modules. +// Top-level build file where you can add configuration options common to all sub-projects/modules. plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.kotlin.android) apply false + // Must come before the others for the jnilibs folder to be automatically detected + alias(libs.plugins.mozilla.rust.android) apply false alias(libs.plugins.kotlin.compose) apply false alias(libs.plugins.kotlin.kapt) apply false alias(libs.plugins.kotlin.serialization) apply false diff --git a/common/src/main/aidl/ADD_TO_PROGUARD.txt b/common/src/main/aidl/ADD_TO_PROGUARD.txt new file mode 100644 index 0000000000..c89cbd2713 --- /dev/null +++ b/common/src/main/aidl/ADD_TO_PROGUARD.txt @@ -0,0 +1 @@ +do not forget to add exemptions for AIDL files to proguard \ No newline at end of file diff --git a/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl similarity index 63% rename from common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl rename to common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl index 8ea6d7f489..daeafbd464 100644 --- a/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.aidl +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/EvdevDeviceInfo.aidl @@ -1,3 +1,3 @@ package io.github.sds100.keymapper.common.models; -parcelable EvdevDeviceHandle; \ No newline at end of file +parcelable EvdevDeviceInfo; \ No newline at end of file diff --git a/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl new file mode 100644 index 0000000000..1959d02203 --- /dev/null +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.aidl @@ -0,0 +1,3 @@ +package io.github.sds100.keymapper.common.models; + +parcelable GrabTargetKeyCode; diff --git a/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl new file mode 100644 index 0000000000..2ad7314522 --- /dev/null +++ b/common/src/main/aidl/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.aidl @@ -0,0 +1,3 @@ +package io.github.sds100.keymapper.common.models; + +parcelable GrabbedDeviceHandle; \ No newline at end of file diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/common/models/ShellResult.aidl b/common/src/main/aidl/io/github/sds100/keymapper/common/models/ShellResult.aidl similarity index 100% rename from sysbridge/src/main/aidl/io/github/sds100/keymapper/common/models/ShellResult.aidl rename to common/src/main/aidl/io/github/sds100/keymapper/common/models/ShellResult.aidl diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt new file mode 100644 index 0000000000..b1d07c5d7a --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabDeviceRequest.kt @@ -0,0 +1,46 @@ +package io.github.sds100.keymapper.common.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable + +@Serializable +@Parcelize +data class GrabDeviceRequest( + val name: String, + val bus: Int, + val vendor: Int, + val product: Int, + /** + * The Android key codes to add support for from this device. + * + * One can request the duplicated uinput device to support extra key codes that the + * original device doesn't support. This is needed when a key map for this device + * inputs a key code that isn't supported by the original evdev device. + */ + val extraKeyCodes: IntArray, +) : Parcelable { + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GrabDeviceRequest + + if (name != other.name) return false + if (bus != other.bus) return false + if (vendor != other.vendor) return false + if (product != other.product) return false + if (!extraKeyCodes.contentEquals(other.extraKeyCodes)) return false + + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + bus.hashCode() + result = 31 * result + vendor.hashCode() + result = 31 * result + product.hashCode() + result = 31 * result + extraKeyCodes.contentHashCode() + return result + } +} diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt new file mode 100644 index 0000000000..c1e436d9a7 --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabTargetKeyCode.kt @@ -0,0 +1,58 @@ +package io.github.sds100.keymapper.common.models + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize +import kotlinx.serialization.Serializable + +@Serializable +@Parcelize +data class GrabTargetKeyCode( + val name: String, + val bus: Int, + val vendor: Int, + val product: Int, + /** + * The Android key codes to add support for from this device. + * + * One can request the duplicated uinput device to support extra key codes that the + * original device doesn't support. This is needed when a key map for this device + * inputs a key code that isn't supported by the original evdev device. + */ + val extraKeyCodes: IntArray, +) : Parcelable { + + constructor( + device: EvdevDeviceInfo, + extraKeyCodes: IntArray, + ) : this( + device.name, + device.bus, + device.vendor, + device.product, + extraKeyCodes, + ) + + override fun equals(other: Any?): Boolean { + if (this === other) return true + if (javaClass != other?.javaClass) return false + + other as GrabTargetKeyCode + + if (name != other.name) return false + if (bus != other.bus) return false + if (vendor != other.vendor) return false + if (product != other.product) return false + if (!extraKeyCodes.contentEquals(other.extraKeyCodes)) return false + + return true + } + + override fun hashCode(): Int { + var result = name.hashCode() + result = 31 * result + bus.hashCode() + result = 31 * result + vendor.hashCode() + result = 31 * result + product.hashCode() + result = 31 * result + extraKeyCodes.contentHashCode() + return result + } +} diff --git a/common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt similarity index 64% rename from common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt rename to common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt index 1b14c03b86..a9d1855a2d 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/models/EvdevDeviceHandle.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/models/GrabbedDeviceHandle.kt @@ -4,11 +4,8 @@ import android.os.Parcelable import kotlinx.parcelize.Parcelize @Parcelize -data class EvdevDeviceHandle( - /** - * The path to the device. E.g /dev/input/event1 - */ - val path: String, +data class GrabbedDeviceHandle( + val id: Int, val name: String, val bus: Int, val vendor: Int, diff --git a/common/src/main/java/io/github/sds100/keymapper/common/utils/Clock.kt b/common/src/main/java/io/github/sds100/keymapper/common/utils/Clock.kt new file mode 100644 index 0000000000..cd98addbd5 --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/utils/Clock.kt @@ -0,0 +1,16 @@ +package io.github.sds100.keymapper.common.utils + +import android.os.SystemClock +import javax.inject.Inject +import javax.inject.Singleton + +@Singleton +class ClockImpl @Inject constructor() : Clock { + override fun elapsedRealtime(): Long { + return SystemClock.elapsedRealtime() + } +} + +interface Clock { + fun elapsedRealtime(): Long +} diff --git a/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt b/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt index 9ba395cf9c..d06a8fe6fc 100644 --- a/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt +++ b/common/src/main/java/io/github/sds100/keymapper/common/utils/Constants.kt @@ -3,7 +3,7 @@ package io.github.sds100.keymapper.common.utils import android.os.Build object Constants { - const val MIN_API: Int = Build.VERSION_CODES.LOLLIPOP + const val MIN_API: Int = Build.VERSION_CODES.O const val MAX_API: Int = 1000 const val SYSTEM_BRIDGE_MIN_API = Build.VERSION_CODES.Q } diff --git a/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt b/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt new file mode 100644 index 0000000000..a89aa9a2d2 --- /dev/null +++ b/common/src/main/java/io/github/sds100/keymapper/common/utils/PhysicalOrientation.kt @@ -0,0 +1,12 @@ +package io.github.sds100.keymapper.common.utils + +/** + * Represents the physical orientation of the device based on the device's + * orientation sensor (accelerometer), independent of the screen rotation setting. + */ +enum class PhysicalOrientation { + PORTRAIT, + LANDSCAPE, + PORTRAIT_INVERTED, + LANDSCAPE_INVERTED, +} diff --git a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt index f13bec696e..a51d8d1e9d 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/Keys.kt @@ -2,14 +2,19 @@ package io.github.sds100.keymapper.data import androidx.datastore.preferences.core.booleanPreferencesKey import androidx.datastore.preferences.core.intPreferencesKey +import androidx.datastore.preferences.core.longPreferencesKey import androidx.datastore.preferences.core.stringPreferencesKey import androidx.datastore.preferences.core.stringSetPreferencesKey object Keys { val darkTheme = stringPreferencesKey("pref_dark_theme_mode") + /** + * libsu was not used in version 3.2.1 and older. The user had to manually enable a setting + * for root features to be enabled. + */ @Deprecated("Now use the libsu library to detect whether the device is rooted.") - val hasRootPermission = booleanPreferencesKey("pref_allow_root_features") + val hasRootPermissionLegacy = booleanPreferencesKey("pref_allow_root_features") val shownAppIntro = booleanPreferencesKey("pref_first_time") @@ -55,9 +60,9 @@ object Keys { booleanPreferencesKey("key_shown_sequence_trigger_explanation_dialog") val shownTriggerConstraintsTip = booleanPreferencesKey("key_shown_trigger_constraints_tip") - val shownCapsLockProModeTip = + val shownCapsLockExpertModeTip = booleanPreferencesKey("key_shown_caps_lock_pro_mode_compatibility_tip") - val shownVolumeButtonsProModeTip = + val shownVolumeButtonsExpertModeTip = booleanPreferencesKey("key_shown_volume_buttons_pro_mode_tip") val shownScreenPinningTip = booleanPreferencesKey("key_shown_screen_pinning_tip") @@ -117,27 +122,34 @@ object Keys { // val skipTapTargetTutorial = // booleanPreferencesKey("key_skip_tap_target_tutorial") - val isProModeWarningUnderstood = + val isExpertModeWarningUnderstood = booleanPreferencesKey("key_is_pro_mode_warning_understood") - val isProModeInteractiveSetupAssistantEnabled = + val isExpertModeInteractiveSetupAssistantEnabled = booleanPreferencesKey("key_is_pro_mode_setup_assistant_enabled") - val isProModeInfoDismissed = + val isExpertModeInfoDismissed = booleanPreferencesKey("key_is_pro_mode_info_dismissed") - val isProModeAutoStartBootEnabled = + val isSystemBridgeKeepAliveEnabled = booleanPreferencesKey("key_is_pro_mode_auto_start_boot_enabled") val isSystemBridgeEmergencyKilled = booleanPreferencesKey("key_is_system_bridge_emergency_killed") + val isSystemBridgeStoppedByUser = + booleanPreferencesKey("key_is_system_bridge_stopped_by_user") + /** * Whether the user has started the system bridge before. */ val isSystemBridgeUsed = booleanPreferencesKey("key_is_system_bridge_used") - val isCleanShutdown = booleanPreferencesKey("key_is_clean_shutdown") + /** + * The last time the system bridge was auto started in time since boot. + * Uses SystemClock.elapsedRealtime(). + */ + val systemBridgeLastAutoStartTime = longPreferencesKey("key_system_bridge_last_auto_start_time") val keyEventActionsUseSystemBridge = booleanPreferencesKey("key_key_event_actions_use_system_bridge") @@ -145,10 +157,10 @@ object Keys { val shellCommandScriptText = stringPreferencesKey("key_shell_command_script_text") /** - * This is stored as true when PRO Mode has been auto started after upgrading - * to 4.0 on a rooted device. + * This is stored as true when PRO Mode has been auto started after updating + * to 4.0 and Key Mapper previously had root or shizuku permission. */ - val handledRootToProModeUpgrade = booleanPreferencesKey("key_handled_root_to_pro_mode_upgrade") + val handledUpgradeToExpertMode = booleanPreferencesKey("key_handled_upgrade_to_pro_mode") val handledMigrateScreenOffKeyMapsNotification = booleanPreferencesKey("key_handled_migrate_screen_off_key_maps_notification") diff --git a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt index ae803a96b4..f241647dd6 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/PreferenceDefaults.kt @@ -15,11 +15,9 @@ object PreferenceDefaults { const val SEQUENCE_TRIGGER_TIMEOUT = 1000 const val HOLD_DOWN_DURATION = 1000 - const val PRO_MODE_INTERACTIVE_SETUP_ASSISTANT = true + const val EXPERT_MODE_INTERACTIVE_SETUP_ASSISTANT = true - // Enable this by default so that key maps will still work for root users - // who upgrade to version 4.0. - const val PRO_MODE_AUTOSTART_BOOT = true + const val EXPERT_MODE_KEEP_ALIVE = true // It is false by default and the first time they turn on the system bridge, // the preference will be set to true. diff --git a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt index 24d5954c60..3deebac611 100644 --- a/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt +++ b/data/src/main/java/io/github/sds100/keymapper/data/entities/ConstraintEntity.kt @@ -58,6 +58,13 @@ data class ConstraintEntity( const val ORIENTATION_PORTRAIT = "constraint_orientation_portrait" const val ORIENTATION_LANDSCAPE = "constraint_orientation_landscape" + const val PHYSICAL_ORIENTATION_PORTRAIT = "constraint_physical_orientation_portrait" + const val PHYSICAL_ORIENTATION_LANDSCAPE = "constraint_physical_orientation_landscape" + const val PHYSICAL_ORIENTATION_PORTRAIT_INVERTED = + "constraint_physical_orientation_portrait_inverted" + const val PHYSICAL_ORIENTATION_LANDSCAPE_INVERTED = + "constraint_physical_orientation_landscape_inverted" + const val FLASHLIGHT_ON = "flashlight_on" const val FLASHLIGHT_OFF = "flashlight_off" diff --git a/evdev/.gitignore b/evdev/.gitignore new file mode 100644 index 0000000000..48b4c6e25e --- /dev/null +++ b/evdev/.gitignore @@ -0,0 +1,5 @@ +/build +.cxx +/src/main/cpp/libevdev/event-names.h +/src/main/cpp/aidl/* +/src/main/rust/**/target/ diff --git a/evdev/.idea/.gitignore b/evdev/.idea/.gitignore new file mode 100644 index 0000000000..13566b81b0 --- /dev/null +++ b/evdev/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/evdev/.idea/dictionaries/project.xml b/evdev/.idea/dictionaries/project.xml new file mode 100644 index 0000000000..77c05f2c6b --- /dev/null +++ b/evdev/.idea/dictionaries/project.xml @@ -0,0 +1,7 @@ + + + + uinput + + + \ No newline at end of file diff --git a/evdev/.idea/gradle.xml b/evdev/.idea/gradle.xml new file mode 100644 index 0000000000..e77e5a3224 --- /dev/null +++ b/evdev/.idea/gradle.xml @@ -0,0 +1,12 @@ + + + + + + + \ No newline at end of file diff --git a/evdev/.idea/inspectionProfiles/Project_Default.xml b/evdev/.idea/inspectionProfiles/Project_Default.xml new file mode 100644 index 0000000000..c5e1cad975 --- /dev/null +++ b/evdev/.idea/inspectionProfiles/Project_Default.xml @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/evdev/.idea/misc.xml b/evdev/.idea/misc.xml new file mode 100644 index 0000000000..e8e8dad566 --- /dev/null +++ b/evdev/.idea/misc.xml @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/evdev/.idea/rust.xml b/evdev/.idea/rust.xml new file mode 100644 index 0000000000..7bc91eafa3 --- /dev/null +++ b/evdev/.idea/rust.xml @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/evdev/.idea/vcs.xml b/evdev/.idea/vcs.xml new file mode 100644 index 0000000000..6c0b863585 --- /dev/null +++ b/evdev/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/evdev/build.gradle.kts b/evdev/build.gradle.kts new file mode 100644 index 0000000000..20a27bb7af --- /dev/null +++ b/evdev/build.gradle.kts @@ -0,0 +1,141 @@ +plugins { + alias(libs.plugins.android.library) + alias(libs.plugins.mozilla.rust.android) +} + +android { + namespace = "io.github.sds100.keymapper.evdev" + compileSdk = libs.versions.compile.sdk.get().toInt() + + // IMPORTANT! If you change this, also change it in build.rs for evdev crate and any CI workflows. This must + // match one of the NDK versions installed in the GitHub actions runner. E.g look here for Ubuntu 24.04 + // https://github.com/actions/runner-images/blob/main/images/ubuntu/Ubuntu2404-Readme.md + ndkVersion = "27.3.13750724" + + defaultConfig { + // System bridge originally only supported Android 10+ because that was the min sdk + // for binder-ndk library. Even though that is no longer used, it is extra effort to support + // the changes to the internal Android APIs on Android 8 and 9. + minSdk = 29 + + testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" + consumerProguardFiles("consumer-rules.pro") + } + + buildTypes { + release { + proguardFiles( + getDefaultProguardFile("proguard-android-optimize.txt"), + "proguard-rules.pro", + ) + } + } + + buildFeatures { + // Disable because a Java implementation of IEvdevCallback is not required in this module + aidl = false + } + + packaging { + jniLibs { + // This replaces extractNativeLibs option in the manifest. This is needed so the + // libraries are extracted to a location on disk where the system bridge process + // can access them. Start in Android 6.0, they are no longer extracted by default. + useLegacyPackaging = true + + // This is required on Android 15. Otherwise a java.lang.UnsatisfiedLinkError: dlopen failed: empty/missing DT_HASH/DT_GNU_HASH error is thrown. + keepDebugSymbols.add("**/*.so") + } + } +} + +// IMPORTANT! Specify path to rustc and cargo in local.properties with the following lines: +// rust.cargoCommand=/Users/username/.cargo/bin/cargo +// rust.rustcCommand=/Users/username/.cargo/bin/rustc +cargo { + val isDebug = gradle.startParameter.taskNames.any { it.lowercase().contains("debug") } + + module = "src/main/rust/evdev_manager" + libname = "evdev_manager" + targets = if (isDebug) { + // Only building for one target saves some time when developing + listOf("arm64") + } else { + listOf("arm", "arm64", "x86", "x86_64") + } + + // Can not do this with buildType configurations. + // See https://github.com/mozilla/rust-android-gradle/issues/38 + profile = if (isDebug) { + "debug" + } else { + "release" + } +} + +dependencies { +} + +// The list of event names needs to be parsed from the input.h file in the NDK. +// input.h can be found in the Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/linux/input.h +// folder on macOS. +val generateLibEvDevEventNames by tasks.registering(Exec::class) { + group = "build" + description = "Generates event names header from input.h" + + val prebuiltDir = File(android.ndkDirectory, "toolchains/llvm/prebuilt") + + // The "darwin-x86_64" part of the path is different on each operating system but it seems like + // the SDK Manager only downloads the NDK specific to the local operating system. So, just + // go into the only directory that the "prebuilt" directory contains. + val hostDirs = prebuiltDir.listFiles { file -> file.isDirectory } + ?: throw GradleException("No prebuilt toolchain directories found in $prebuiltDir") + + if (hostDirs.size != 1) { + throw GradleException( + "Expected exactly one prebuilt toolchain directory in $prebuiltDir, found ${hostDirs.size}", + ) + } + val toolchainDir = hostDirs[0].absolutePath + + val inputHeader = "$toolchainDir/sysroot/usr/include/linux/input.h" + val inputEventCodesHeader = "$toolchainDir/sysroot/usr/include/linux/input-event-codes.h" + val outputHeader = "$projectDir/src/main/cpp/libevdev/event-names.h" + val pythonScript = "$projectDir/src/main/cpp/libevdev/make-event-names.py" + + commandLine("python3", pythonScript, inputHeader, inputEventCodesHeader) + + standardOutput = File(outputHeader).outputStream() + + inputs.file(pythonScript) + inputs.file(inputHeader) + inputs.file(inputEventCodesHeader) + outputs.file(outputHeader) +} + +// Note: NDK AIDL compilation is no longer needed since we're using pure JNI +// instead of C++ Binder layer. The Kotlin side still uses IEvdevCallback AIDL, +// but that's handled by Android's standard AIDL processing. + +tasks.named("preBuild") { + dependsOn(generateLibEvDevEventNames) +} + +// Ensure event names are generated before cargo build runs +afterEvaluate { + tasks.matching { it.name.contains("cargoBuild") }.configureEach { + dependsOn(generateLibEvDevEventNames) + } +} + +// Must come after all tasks above, otherwise gradle syncing fails. +// +// Run cargo build when the files change. +// See https://github.com/mozilla/rust-android-gradle/issues/166 +tasks.whenTaskAdded { + if (name == "mergeDebugJniLibFolders" || name == "mergeReleaseJniLibFolders") { + outputs.upToDateWhen { false } + + dependsOn("cargoBuild") + } +} diff --git a/evdev/consumer-rules.pro b/evdev/consumer-rules.pro new file mode 100644 index 0000000000..e69de29bb2 diff --git a/evdev/proguard-rules.pro b/evdev/proguard-rules.pro new file mode 100644 index 0000000000..481bb43481 --- /dev/null +++ b/evdev/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/sysbridge/src/main/cpp/libevdev/Makefile.am b/evdev/src/main/cpp/libevdev/Makefile.am similarity index 100% rename from sysbridge/src/main/cpp/libevdev/Makefile.am rename to evdev/src/main/cpp/libevdev/Makefile.am diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-int.h b/evdev/src/main/cpp/libevdev/libevdev-int.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-int.h rename to evdev/src/main/cpp/libevdev/libevdev-int.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-names.c b/evdev/src/main/cpp/libevdev/libevdev-names.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-names.c rename to evdev/src/main/cpp/libevdev/libevdev-names.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput-int.h b/evdev/src/main/cpp/libevdev/libevdev-uinput-int.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput-int.h rename to evdev/src/main/cpp/libevdev/libevdev-uinput-int.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput.c b/evdev/src/main/cpp/libevdev/libevdev-uinput.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput.c rename to evdev/src/main/cpp/libevdev/libevdev-uinput.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-uinput.h b/evdev/src/main/cpp/libevdev/libevdev-uinput.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-uinput.h rename to evdev/src/main/cpp/libevdev/libevdev-uinput.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev-util.h b/evdev/src/main/cpp/libevdev/libevdev-util.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev-util.h rename to evdev/src/main/cpp/libevdev/libevdev-util.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.c b/evdev/src/main/cpp/libevdev/libevdev.c similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.c rename to evdev/src/main/cpp/libevdev/libevdev.c diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.h b/evdev/src/main/cpp/libevdev/libevdev.h similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.h rename to evdev/src/main/cpp/libevdev/libevdev.h diff --git a/sysbridge/src/main/cpp/libevdev/libevdev.sym b/evdev/src/main/cpp/libevdev/libevdev.sym similarity index 100% rename from sysbridge/src/main/cpp/libevdev/libevdev.sym rename to evdev/src/main/cpp/libevdev/libevdev.sym diff --git a/sysbridge/src/main/cpp/libevdev/make-event-names.py b/evdev/src/main/cpp/libevdev/make-event-names.py old mode 100755 new mode 100644 similarity index 99% rename from sysbridge/src/main/cpp/libevdev/make-event-names.py rename to evdev/src/main/cpp/libevdev/make-event-names.py index 743b4b58b1..16a984d960 --- a/sysbridge/src/main/cpp/libevdev/make-event-names.py +++ b/evdev/src/main/cpp/libevdev/make-event-names.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python3 +#!/usr/bin/_env python3 # # Parses linux/input.h scanning for #define KEY_FOO 134 # Prints C header files or Python files that can be used as diff --git a/evdev/src/main/rust/evdev/.cargo/config.toml b/evdev/src/main/rust/evdev/.cargo/config.toml new file mode 100644 index 0000000000..f2084c6b7f --- /dev/null +++ b/evdev/src/main/rust/evdev/.cargo/config.toml @@ -0,0 +1,15 @@ +[target.armv7-linux-androideabi] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.aarch64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.i686-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.x86_64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + + + + diff --git a/evdev/src/main/rust/evdev/Cargo.lock b/evdev/src/main/rust/evdev/Cargo.lock new file mode 100644 index 0000000000..e5c3b3271b --- /dev/null +++ b/evdev/src/main/rust/evdev/Cargo.lock @@ -0,0 +1,53 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "cc" +version = "1.2.46" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b97463e1064cb1b1c1384ad0a0b9c8abd0988e2a91f52606c80ef14aadb63e36" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "evdev" +version = "0.1.0" +dependencies = [ + "bitflags", + "cc", + "libc", + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "libc" +version = "1.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7222002e5385b4d9327755661e3847c970e8fbf9dea6da8c57f16e8cfbff53a8" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" diff --git a/evdev/src/main/rust/evdev/Cargo.toml b/evdev/src/main/rust/evdev/Cargo.toml new file mode 100644 index 0000000000..489211c191 --- /dev/null +++ b/evdev/src/main/rust/evdev/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "evdev" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev" +# "cdylib" is necessary to produce a C-style dynamic library that can be loaded by Android's JNI. +# "rlib" is necessary to allow this crate to be used as a dependency by other Rust crates. +crate-type = ["cdylib", "rlib"] + +[profile.dev] +debug = true +strip = false # Don't strip symbols, keeps them in the .so file + +[dependencies] +libc = "1.0.0-alpha.1" +log = "0.4.28" +bitflags = "2.10.0" + +[build-dependencies] +cc = "1.2.46" diff --git a/evdev/src/main/rust/evdev/README.md b/evdev/src/main/rust/evdev/README.md new file mode 100644 index 0000000000..f96a1eb7e9 --- /dev/null +++ b/evdev/src/main/rust/evdev/README.md @@ -0,0 +1 @@ +Most of the code in this crate is taken from https://github.com/ndesh26/evdev-rs. \ No newline at end of file diff --git a/evdev/src/main/rust/evdev/build.rs b/evdev/src/main/rust/evdev/build.rs new file mode 100644 index 0000000000..c4813c4800 --- /dev/null +++ b/evdev/src/main/rust/evdev/build.rs @@ -0,0 +1,133 @@ +use std::env; +use std::path::{Path, PathBuf}; + +fn main() { + let manifest_dir: PathBuf = PathBuf::from(env!("CARGO_MANIFEST_DIR")); + + // Map Rust target architecture to Android ABI directory + let target = env::var("TARGET").expect("TARGET environment variable not set"); + + // This crate only supports Android targets for actual builds. + // For cargo check on host systems, we'll skip C/C++ compilation but still + // generate bindings to allow type checking. + let is_android = target.contains("android"); + + if !is_android { + eprintln!( + "Warning: Building for non-Android target '{}'. Skipping C/C++ compilation.", + target + ); + eprintln!("This crate is designed for Android. Use Gradle for actual builds."); + // Skip all compilation but succeed to allow cargo check to work + return; + } + + // Path to C/C++ source files + let cpp_dir = manifest_dir.join("../../cpp"); + + println!("cargo:rerun-if-changed={}", cpp_dir.to_str().unwrap()); + + // Find Android NDK sysroot for bindgen + let ndk_sysroot = find_ndk_sysroot(&manifest_dir); + let sysroot_include = ndk_sysroot.join("usr/include"); + + let libevdev_dir = cpp_dir.join("libevdev"); + + // Build C files from libevdev + let mut c_builder = cc::Build::new(); + c_builder + .file(libevdev_dir.join("libevdev.c")) + .file(libevdev_dir.join("libevdev-names.c")) + .file(libevdev_dir.join("libevdev-uinput.c")) + .include(&libevdev_dir) + .include(sysroot_include.join("linux/input-event-codes.h")) + .flag("-Werror=format") + .flag("-Wno-unused-parameter") + .flag("-fdata-sections") + .flag("-ffunction-sections"); + + if env::var("PROFILE").unwrap() == "release" { + c_builder.flag("-O2").flag("-fvisibility=hidden"); + } + + c_builder.compile("evdev_c"); +} + +fn find_ndk_sysroot(manifest_dir: &Path) -> PathBuf { + let sdk_dir = get_sdk_dir(manifest_dir).expect("SDK directory not available"); + let ndk_version = "27.3.13750724"; + + get_sysroot_for_version(&sdk_dir, ndk_version) +} + +fn get_sdk_dir(manifest_dir: &Path) -> Option { + // 1. Read from local.properties file + // Navigate from evdev crate to project root + let local_properties = manifest_dir.join("../../../../../local.properties"); + + if let Ok(contents) = std::fs::read_to_string(&local_properties) { + for line in contents.lines() { + // Skip comments and empty lines + let line = line.trim(); + if line.is_empty() || line.starts_with('#') { + continue; + } + + // Look for sdk.dir=value or android.sdk.dir=value + if let Some(stripped) = line.strip_prefix("sdk.dir=") { + let sdk_path = stripped.trim(); + if !sdk_path.is_empty() { + return Some(sdk_path.to_string()); + } + } + if let Some(stripped) = line.strip_prefix("android.sdk.dir=") { + let sdk_path = stripped.trim(); + if !sdk_path.is_empty() { + return Some(sdk_path.to_string()); + } + } + } + } + + // 2. Check environment variables + if let Ok(sdk_dir) = env::var("ANDROID_SDK_ROOT") { + return Some(sdk_dir); + } + + if let Ok(sdk_dir) = env::var("ANDROID_HOME") { + return Some(sdk_dir); + } + + None +} + +fn get_sysroot_for_version(sdk_dir: &str, version: &str) -> PathBuf { + // Detect host platform + let host = if cfg!(target_os = "macos") { + "darwin-x86_64" + } else if cfg!(target_os = "linux") { + "linux-x86_64" + } else if cfg!(target_os = "windows") { + "windows-x86_64" + } else { + panic!("Unsupported host platform for NDK") + }; + + let sysroot = PathBuf::from(sdk_dir) + .join("ndk") + .join(version) + .join("toolchains") + .join("llvm") + .join("prebuilt") + .join(host) + .join("sysroot"); + + if !sysroot.exists() { + panic!( + "Could not find Android NDK sysroot for version {} at {}. Please ensure NDK {} is installed in {}/ndk/", + version, sysroot.display(), version, sdk_dir + ); + } + + sysroot +} diff --git a/evdev/src/main/rust/evdev/src/device.rs b/evdev/src/main/rust/evdev/src/device.rs new file mode 100644 index 0000000000..da1d135206 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/device.rs @@ -0,0 +1,900 @@ +use crate::{AbsInfo, GrabMode, InputEvent, LedState, ReadFlag, ReadStatus, TimeVal}; +use libc::{c_int, c_uint, c_void}; +use libc::{fcntl, F_GETFD}; +use std::ffi::CString; +use std::fs::File; +use std::fs::OpenOptions; +use std::io::Read; +use std::mem::ManuallyDrop; +use std::os::fd::AsFd; +use std::os::unix::fs::OpenOptionsExt; +use std::os::unix::io::{AsRawFd, FromRawFd, RawFd}; +use std::path::Path; +use std::{io, ptr}; + +use crate::enums::*; +use crate::libevdev; +use crate::util::*; + +/// Types that can be enabled on a DeviceWrapper (i.e. buttons, keys, relative motion) +/// +/// Generally this method will not be called directly, but will insted be called through [Device::enable()](crate::Device::enable) +/// +/// ```rust +/// # use evdev_rs::{UninitDevice, DeviceWrapper, Enable, enums::{EventCode, EV_REL::REL_X}}; +/// let dev = UninitDevice::new().expect("Device creation failed"); +/// dev.enable(EventCode::EV_REL(REL_X)).expect("Enable failed"); +/// ``` +/// +/// If you need to enable a EV_ABS or EV_REP event code, use +/// [enable_event_code](crate::Device::enable_event_code), as this +/// implementation doesn't pass EV_ABS data. +pub trait Enable { + fn enable(&self, device: &D) -> io::Result<()>; + fn disable(&self, device: &D) -> io::Result<()>; + fn has(&self, device: &D) -> bool; +} + +impl Enable for InputProp { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_property(self) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_property(self) + } + fn has(&self, device: &D) -> bool { + device.has_property(self) + } +} + +impl Enable for EventType { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_event_type(self) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_event_type(self) + } + fn has(&self, device: &D) -> bool { + device.has_event_type(self) + } +} + +impl Enable for EventCode { + fn enable(&self, device: &D) -> io::Result<()> { + device.enable_event_code(self, None) + } + fn disable(&self, device: &D) -> io::Result<()> { + device.disable_event_code(self) + } + fn has(&self, device: &D) -> bool { + device.has_event_code(self) + } +} + +/// Extra data for use with enable_event_code +#[derive(Clone, Copy, Debug)] +pub enum EnableCodeData { + AbsInfo(AbsInfo), + RepInfo(i32), +} + +/// Abstraction over structs which contain an inner `*mut libevdev` +pub trait DeviceWrapper: Sized { + fn raw(&self) -> *mut libevdev::libevdev; + + /// Forcibly enable an EventType/InputProp on this device, even if the underlying + /// device does not support it. While this cannot make the device actually + /// report such events, it will now return true for has(). + /// + /// This is a local modification only affecting only this representation of + /// this device. + fn enable(&self, e: E) -> io::Result<()> { + e.enable(self) + } + + /// Enables this property, a call to `set_file` will overwrite any previously set values + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_property(&self, prop: &InputProp) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_enable_property(self.raw(), *prop as c_uint) as i32 }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Forcibly enable an event type on this device, even if the underlying + /// device does not support it. While this cannot make the device actually + /// report such events, it will now return true for libevdev_has_event_type(). + /// + /// This is a local modification only affecting only this representation of + /// this device. + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_event_type(&self, ev_type: &EventType) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_enable_event_type(self.raw(), *ev_type as c_uint) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Forcibly enable an event type on this device, even if the underlying + /// device does not support it. While this cannot make the device actually + /// report such events, it will now return true for libevdev_has_event_code(). + /// + /// The last argument depends on the type and code: + /// If type is EV_ABS, data must be a pointer to a struct input_absinfo + /// containing the data for this axis. + /// If type is EV_REP, data must be a pointer to a int containing the data + /// for this axis. + /// For all other types, the argument must be `None`. + /// + /// Note: Please use the `enable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn enable_event_code( + &self, + ev_code: &EventCode, + data: Option, + ) -> io::Result<()> { + let data = match ev_code { + EventCode::EV_ABS(_) => match data { + Some(EnableCodeData::AbsInfo(info)) => &info.as_raw() as *const _ as *const c_void, + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "EventCode::EV_ABS must be paired with EnableCodeData::AbsInfo", + )) + } + }, + EventCode::EV_REP(_) => match data { + Some(EnableCodeData::RepInfo(info)) => { + &libc::c_int::from(info) as *const _ as *const c_void + } + _ => { + return Err(io::Error::new( + io::ErrorKind::InvalidInput, + "EventCode::EV_REP must be paired with EnableCodeData::RepInfo", + )) + } + }, + _ => ptr::null(), + }; + + let (ev_type, ev_code) = event_code_to_int(ev_code); + + let result = + unsafe { libevdev::libevdev_enable_event_code(self.raw(), ev_type, ev_code, data) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Forcibly disable an EventType/EventCode on this device, even if the + /// underlying device provides it. This effectively mutes the respective set of + /// events. has() will return false for this EventType/EventCode + /// + /// In most cases, a caller likely only wants to disable a single code, not + /// the whole type. + /// + /// Disabling EV_SYN will not work. In Peter's Words "Don't shoot yourself + /// in the foot. It hurts". + /// + /// This is a local modification only affecting only this representation of + /// this device. + fn disable(&self, d: E) -> io::Result<()> { + d.disable(self) + } + + /// Forcibly disable an event type on this device, even if the underlying + /// device provides it. This effectively mutes the respective set of + /// events. libevdev will filter any events matching this type and none will + /// reach the caller. libevdev_has_event_type() will return false for this + /// type. + /// + /// In most cases, a caller likely only wants to disable a single code, not + /// the whole type. Use `disable_event_code` for that. + /// + /// Disabling EV_SYN will not work. In Peter's Words "Don't shoot yourself + /// in the foot. It hurts". + /// + /// This is a local modification only affecting only this representation of + /// this device. + /// + /// Note: Please use the `disable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn disable_event_type(&self, ev_type: &EventType) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_disable_event_type(self.raw(), *ev_type as c_uint) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + /// Forcibly disable an event code on this device, even if the underlying + /// device provides it. This effectively mutes the respective set of + /// events. libevdev will filter any events matching this type and code and + /// none will reach the caller. `has_event_code` will return false for + /// this code. + /// + /// Disabling all event codes for a given type will not disable the event + /// type. Use `disable_event_type` for that. + /// + /// This is a local modification only affecting only this representation of + /// this device. + /// + /// Disabling codes of type EV_SYN will not work. Don't shoot yourself in the + /// foot. It hurts. + /// + /// Note: Please use the `disable` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn disable_event_code(&self, code: &EventCode) -> io::Result<()> { + let (ev_type, ev_code) = event_code_to_int(code); + let result = unsafe { libevdev::libevdev_disable_event_code(self.raw(), ev_type, ev_code) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + fn disable_property(&self, prop: &InputProp) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_disable_property(self.raw(), (*prop) as c_uint) }; + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Returns `true` if device support the InputProp/EventType/EventCode and false otherwise + fn has(&self, e: E) -> bool { + e.has(self) + } + + /// Returns `true` if device support the property and false otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_property(&self, prop: &InputProp) -> bool { + unsafe { libevdev::libevdev_has_property(self.raw(), *prop as c_uint) != 0 } + } + + /// Returns `true` is the device support this event type and `false` otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_event_type(&self, ev_type: &EventType) -> bool { + unsafe { libevdev::libevdev_has_event_type(self.raw(), *ev_type as c_uint) != 0 } + } + + /// Return `true` is the device support this event type and code and `false` otherwise + /// + /// Note: Please use the `has` function instead. This function is only + /// available for the sake of maintaining compatibility with libevdev. + fn has_event_code(&self, code: &EventCode) -> bool { + unsafe { + let (ev_type, ev_code) = event_code_to_int(code); + libevdev::libevdev_has_event_code(self.raw(), ev_type, ev_code) != 0 + } + } + + string_getter!( + #[doc = "Get device's name, as set by the kernel, or overridden by a call to `set_name`"], + name, libevdev_get_name, + #[doc = "Get device's physical location, as set by the kernel, or overridden by a call to `set_phys`"], + phys, libevdev_get_phys, + #[doc = "Get device's unique identifier, as set by the kernel, or overridden by a call to `set_uniq`"], + uniq, libevdev_get_uniq + ); + + string_setter!( + set_name, + libevdev_set_name, + set_phys, + libevdev_set_phys, + set_uniq, + libevdev_set_uniq + ); + + product_getter!( + product_id, + libevdev_get_id_product, + vendor_id, + libevdev_get_id_vendor, + bustype, + libevdev_get_id_bustype, + version, + libevdev_get_id_version + ); + + product_setter!( + set_product_id, + libevdev_set_id_product, + set_vendor_id, + libevdev_set_id_vendor, + set_bustype, + libevdev_set_id_bustype, + set_version, + libevdev_set_id_version + ); + + /// Get the axis info for the given axis, as advertised by the kernel. + /// + /// Returns the `AbsInfo` for the given the code or None if the device + /// doesn't support this code + fn abs_info(&self, code: &EventCode) -> Option { + let (_, ev_code) = event_code_to_int(code); + let a = unsafe { libevdev::libevdev_get_abs_info(self.raw(), ev_code).as_ref()? }; + + Some(AbsInfo { + value: a.value, + minimum: a.minimum, + maximum: a.maximum, + fuzz: a.fuzz, + flat: a.flat, + resolution: a.resolution, + }) + } + + /// Change the abs info for the given EV_ABS event code, if the code exists. + /// + /// This function has no effect if `has_event_code` returns false for + /// this code. + fn set_abs_info(&self, code: &EventCode, absinfo: &AbsInfo) { + let (_, ev_code) = event_code_to_int(code); + + unsafe { + libevdev::libevdev_set_abs_info(self.raw(), ev_code, &absinfo.as_raw() as *const _); + } + } + + /// Returns the current value of the event type. + /// + /// If the device supports this event type and code, the return value is + /// set to the current value of this axis. Otherwise, `None` is returned. + fn event_value(&self, code: &EventCode) -> Option { + let mut value: i32 = 0; + let (ev_type, ev_code) = event_code_to_int(code); + let valid = unsafe { + libevdev::libevdev_fetch_event_value(self.raw(), ev_type, ev_code, &mut value) + }; + + match valid { + 0 => None, + _ => Some(value), + } + } + + /// Set the value for a given event type and code. + /// + /// This only makes sense for some event types, e.g. setting the value for + /// EV_REL is pointless. + /// + /// This is a local modification only affecting only this representation of + /// this device. A future call to event_value() will return this + /// value, unless the value was overwritten by an event. + /// + /// If the device supports ABS_MT_SLOT, the value set for any ABS_MT_* + /// event code is the value of the currently active slot. You should use + /// `set_slot_value` instead. + /// + /// If the device supports ABS_MT_SLOT and the type is EV_ABS and the code is + /// ABS_MT_SLOT, the value must be a positive number less then the number of + /// slots on the device. Otherwise, `set_event_value` returns Err. + fn set_event_value(&self, code: &EventCode, val: i32) -> io::Result<()> { + let (ev_type, ev_code) = event_code_to_int(code); + let result = unsafe { + libevdev::libevdev_set_event_value(self.raw(), ev_type, ev_code, val as c_int) + }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + abs_getter!( + abs_minimum, + libevdev_get_abs_minimum, + abs_maximum, + libevdev_get_abs_maximum, + abs_fuzz, + libevdev_get_abs_fuzz, + abs_flat, + libevdev_get_abs_flat, + abs_resolution, + libevdev_get_abs_resolution + ); + + abs_setter!( + set_abs_minimum, + libevdev_set_abs_minimum, + set_abs_maximum, + libevdev_set_abs_maximum, + set_abs_fuzz, + libevdev_set_abs_fuzz, + set_abs_flat, + libevdev_set_abs_flat, + set_abs_resolution, + libevdev_set_abs_resolution + ); + + /// Return the current value of the code for the given slot. + /// + /// If the device supports this event code, the return value is + /// is set to the current value of this axis. Otherwise, or + /// if the event code is not an ABS_MT_* event code, `None` is returned + fn slot_value(&self, slot: u32, code: &EventCode) -> Option { + let (_, ev_code) = event_code_to_int(code); + let mut value: i32 = 0; + let valid = unsafe { + libevdev::libevdev_fetch_slot_value(self.raw(), slot as c_uint, ev_code, &mut value) + }; + + match valid { + 0 => None, + _ => Some(value), + } + } + + /// Set the value for a given code for the given slot. + /// + /// This is a local modification only affecting only this representation of + /// this device. A future call to `slot_value` will return this value, + /// unless the value was overwritten by an event. + /// + /// This function does not set event values for axes outside the ABS_MT range, + /// use `set_event_value` instead. + fn set_slot_value(&self, slot: u32, code: &EventCode, val: i32) -> io::Result<()> { + let (_, ev_code) = event_code_to_int(code); + let result = unsafe { + libevdev::libevdev_set_slot_value(self.raw(), slot as c_uint, ev_code, val as c_int) + }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Get the number of slots supported by this device. + /// + /// The number of slots supported, or `None` if the device does not provide + /// any slots + /// + /// A device may provide ABS_MT_SLOT but a total number of 0 slots. Hence + /// the return value of `None` for "device does not provide slots at all" + fn num_slots(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_num_slots(self.raw()) }; + + match result { + -1 => None, + slots => Some(slots), + } + } + + /// Get the currently active slot. + /// + /// This may differ from the value an ioctl may return at this time as + /// events may have been read off the file since changing the slot value + /// but those events are still in the buffer waiting to be processed. + /// The returned value is the value a caller would see if it were to + /// process events manually one-by-one. + fn current_slot(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_current_slot(self.raw()) }; + + match result { + -1 => None, + slots => Some(slots), + } + } +} + +/// Opaque struct representing an evdev device with no backing file +pub struct UninitDevice { + raw: *mut libevdev::libevdev, +} + +unsafe impl Send for UninitDevice {} + +impl DeviceWrapper for UninitDevice { + fn raw(&self) -> *mut libevdev::libevdev { + self.raw + } +} + +impl UninitDevice { + /// Initialize a new libevdev device. + /// + /// Generally you should use Device::new_from_file instead of this method + /// This function only initializes the struct to sane default values. + /// To actually hook up the device to a kernel device, use `set_file`. + pub fn new() -> Option { + let libevdev = unsafe { libevdev::libevdev_new() }; + + if libevdev.is_null() { + None + } else { + Some(UninitDevice { raw: libevdev }) + } + } + + /// Set the file for this struct and initialize internal data. + /// + /// If the device changed and you need to re-read a device, use `Device::new_from_file` method. + /// If you need to change the file after + /// closing and re-opening the same device, use `change_file`. + pub fn set_file(self, file: File) -> io::Result { + // Don't call UninitDevice's destructor so we can reuse the inner libevdev + let leak = ManuallyDrop::new(self); + let result = unsafe { libevdev::libevdev_set_fd(leak.raw, file.as_raw_fd()) }; + match result { + 0 => Ok(Device { + file, + raw: leak.raw, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `set_file`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn set_fd(self, file: File) -> io::Result { + self.set_file(file) + } +} + +impl Drop for UninitDevice { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_free(self.raw); + } + } +} + +impl std::fmt::Debug for UninitDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("UninitDevice") + .field("name", &self.name()) + .field("phys", &self.phys()) + .field("uniq", &self.uniq()) + .finish() + } +} + +/// Opaque struct representing an evdev device +/// +/// Unlike libevdev, this `Device` maintains an associated file as an invariant +pub struct Device { + file: File, + raw: *mut libevdev::libevdev, +} + +unsafe impl Send for Device {} + +impl DeviceWrapper for Device { + fn raw(&self) -> *mut libevdev::libevdev { + self.raw + } +} + +impl Device { + /// Initialize a new libevdev device from the given file. + /// + /// This is a shortcut for + /// + /// ```rust,no_run + /// use evdev_rs::{Device, UninitDevice}; + /// # use std::fs::File; + /// + /// let uninit_device = UninitDevice::new().unwrap(); + /// # let file = File::open("/dev/input/event0").unwrap(); + /// let device = uninit_device.set_file(file); + /// ``` + /// + /// The caller is responsible for opening the file and setting + /// the `O_NONBLOCK` flag and handling permissions. + /// If the file is opened without O_NONBLOCK flag then next_event + /// should be called with ReadFlag::BLOCKING. Due to the caching + /// nature of next_event we might block while trying to buffer + /// new events even though some events are already present. + pub fn new_from_file(file: File) -> io::Result { + let mut libevdev = std::ptr::null_mut(); + let result = unsafe { libevdev::libevdev_new_from_fd(file.as_raw_fd(), &mut libevdev) }; + + match result { + 0 => Ok(Device { + file, + raw: libevdev, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `new_from_file`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn new_from_fd(file: File) -> io::Result { + Self::new_from_file(file) + } + + /// Opens a device with the given path as the location of devnode + /// + /// The devnode file is opened with `O_NONBLOCK` and all the pending + /// events are first read from the file before creating the device. + pub fn new_from_path>(path: P) -> io::Result { + let mut file = OpenOptions::new() + .read(true) + .write(false) // Writing is only allowed when rooted so must be false + .custom_flags(libc::O_NONBLOCK) + .open(path)?; + let mut buffer = [0u8; 20 * std::mem::size_of::()]; + + let last_result = loop { + let result = file.read(&mut buffer); + if result.is_err() { + break result; + } + }; + let _error_code = io::Error::from(io::ErrorKind::WouldBlock); + match last_result { + Err(_error_code) => Self::new_from_file(file), + _ => Err(io::Error::new( + io::ErrorKind::WouldBlock, + "Unable to open file with O_NONBLOCK", + )), + } + } + + /// Returns the file associated with the device + pub fn file(&self) -> &File { + &self.file + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `file`. This function can easily be misused. Calling + this method, then dropping the returned file will lead to failures + e.g. next_event will return an Err()" + )] + pub fn fd(&self) -> Option { + let result = unsafe { libevdev::libevdev_get_fd(self.raw) }; + match result { + 0 => None, + _ => unsafe { Some(File::from_raw_fd(result)) }, + } + } + + /// Change the file for this device, without re-reading the actual device. + /// + /// On success, returns the file that was previously associated with this + /// device. + /// + /// If the file changes after initializing the device, for example after a + /// VT-switch in the X.org X server, this function updates the internal + /// file to the newly opened. No check is made that new file points to the + /// same device. If the device has changed, evdev's behavior is undefined. + /// + /// evdev device does not sync itself after changing the file and keeps the + /// current device state. Use next_event with the FORCE_SYNC flag to force + /// a re-sync. + /// + /// # Example + /// + /// ```rust,no_run + /// use evdev_rs::{Device, UninitDevice, ReadFlag, ReadStatus}; + /// # use std::fs::File; + /// # fn hidden() -> std::io::Result<()> { + /// let mut dev = Device::new_from_file(File::open("/dev/input/input0")?)?; + /// dev.change_file(File::open("/dev/input/input1")?)?; + /// dev.next_event(ReadFlag::FORCE_SYNC); + /// while dev.next_event(ReadFlag::SYNC).ok().unwrap().0 == ReadStatus::Sync + /// {} // noop + /// # Ok(()) + /// # } + /// ``` + /// After changing the file, the device is assumed ungrabbed and a caller must + /// call libevdev_grab() again. + pub fn change_file(&mut self, file: File) -> io::Result { + let result = unsafe { libevdev::libevdev_change_fd(self.raw, file.as_raw_fd()) }; + + match result { + 0 => { + let mut file = file; + std::mem::swap(&mut file, &mut self.file); + Ok(file) + } + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer new_from_file. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn change_fd(&mut self, file: File) -> io::Result<()> { + self.change_file(file)?; + Ok(()) + } + + /// Grab or ungrab the device through a kernel EVIOCGRAB. + /// + /// This prevents other clients (including kernel-internal ones such as + /// rfkill) from receiving events from this device. This is generally a + /// bad idea. Don't do this. Grabbing an already grabbed device, or + /// ungrabbing an ungrabbed device is a noop and always succeeds. + /// + /// A grab is an operation tied to a file descriptor, not a device. If a + /// client changes the file descriptor with Device::change_file(), it must + /// also re-issue a grab with libevdev_grab(). + pub fn grab(&mut self, grab: GrabMode) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_grab(self.raw, grab as c_int) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Check if there are events waiting for us. + /// + /// This function does not consume an event and may not access the device + /// file at all. If there are events queued internally this function will + /// return true. If the internal queue is empty, this function will poll + /// the file descriptor for data. + /// + /// This is a convenience function for simple processes, most complex programs + /// are expected to use select(2) or poll(2) on the file descriptor. The kernel + /// guarantees that if data is available, it is a multiple of sizeof(struct + /// input_event), and thus calling `next_event` when select(2) or + /// poll(2) return is safe. You do not need `has_event_pending` if + /// you're using select(2) or poll(2). + pub fn has_event_pending(&self) -> bool { + unsafe { libevdev::libevdev_has_event_pending(self.raw) > 0 } + } + + /// Return the driver version of a device already intialize with `set_file` + pub fn driver_version(&self) -> i32 { + unsafe { libevdev::libevdev_get_driver_version(self.raw) as i32 } + } + + /// Set the device's EV_ABS axis to the value defined in the abs + /// parameter. This will be written to the kernel. + pub fn set_kernel_abs_info(&self, code: &EventCode, absinfo: &AbsInfo) { + let (_, ev_code) = event_code_to_int(code); + + unsafe { + libevdev::libevdev_kernel_set_abs_info( + self.raw, + ev_code, + &absinfo.as_raw() as *const _, + ); + } + } + + /// Turn an LED on or off. + /// + /// enabling an LED requires write permissions on the device's file descriptor. + pub fn kernel_set_led_value(&self, code: &EventCode, value: LedState) -> io::Result<()> { + let (_, ev_code) = event_code_to_int(code); + let result = + unsafe { libevdev::libevdev_kernel_set_led_value(self.raw, ev_code, value as c_int) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Set the clock ID to be used for timestamps. Further events from this device + /// will report an event time based on the given clock. + /// + /// This is a modification only affecting this representation of + /// this device. + pub fn set_clock_id(&self, clockid: i32) -> io::Result<()> { + let result = unsafe { libevdev::libevdev_set_clock_id(self.raw, clockid) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + /// Get the next event from the device. This function operates in two different + /// modes: normal mode or sync mode. + /// + /// In normal mode (when flags has `evdev::NORMAL` set), this function returns + /// `ReadStatus::Success` and returns the event. If no events are available at + /// this time, it returns `-EAGAIN` as `Err`. + /// + /// If the current event is an `EV_SYN::SYN_DROPPED` event, this function returns + /// `ReadStatus::Sync` and is set to the `EV_SYN` event.The caller should now call + /// this function with the `evdev::SYNC` flag set, to get the set of events that + /// make up the device state delta. This function returns ReadStatus::Sync for + /// each event part of that delta, until it returns `-EAGAIN` once all events + /// have been synced. + /// + /// If a device needs to be synced by the caller but the caller does not call + /// with the `evdev::SYNC` flag set, all events from the diff are dropped after + /// evdev updates its internal state and event processing continues as normal. + /// Note that the current slot and the state of touch points may have updated + /// during the `SYN_DROPPED` event, it is strongly recommended that a caller + /// ignoring all sync events calls `current_slot` and checks the + /// `ABS_MT_TRACKING_ID` values for all slots. + /// + /// If a device has changed state without events being enqueued in evdev, + /// e.g. after changing the file descriptor, use the `evdev::FORCE_SYNC` flag. + /// This triggers an internal sync of the device and `next_event` returns + /// `ReadStatus::Sync`. + pub fn next_event(&self, flags: ReadFlag) -> io::Result<(ReadStatus, InputEvent)> { + let mut ev = libevdev::input_event { + time: libevdev::timeval { + tv_sec: 0, + tv_usec: 0, + }, + type_: 0, + code: 0, + value: 0, + }; + + let result = + unsafe { libevdev::libevdev_next_event(self.raw, flags.bits() as c_uint, &mut ev) }; + + let event = InputEvent { + time: TimeVal { + tv_sec: ev.time.tv_sec, + tv_usec: ev.time.tv_usec, + }, + event_code: int_to_event_code(ev.type_ as u32, ev.code as u32), + value: ev.value, + }; + + match result { + libevdev::LIBEVDEV_READ_STATUS_SUCCESS => Ok((ReadStatus::Success, event)), + libevdev::LIBEVDEV_READ_STATUS_SYNC => Ok((ReadStatus::Sync, event)), + error => Err(io::Error::from_raw_os_error(-error)), + } + } +} + +impl Drop for Device { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_free(self.raw); + } + } +} + +impl std::fmt::Debug for Device { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("Device") + .field("name", &self.name()) + .field("phys", &self.phys()) + .field("uniq", &self.uniq()) + .finish() + } +} + +impl AsRawFd for Device { + fn as_raw_fd(&self) -> RawFd { + self.file.as_raw_fd() + } +} diff --git a/evdev/src/main/rust/evdev/src/enums.rs b/evdev/src/main/rust/evdev/src/enums.rs new file mode 100644 index 0000000000..02831b9335 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/enums.rs @@ -0,0 +1,2713 @@ +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EventType { + EV_SYN = 0, + EV_KEY = 1, + EV_REL = 2, + EV_ABS = 3, + EV_MSC = 4, + EV_SW = 5, + EV_LED = 17, + EV_SND = 18, + EV_REP = 20, + EV_FF = 21, + EV_PWR = 22, + EV_FF_STATUS = 23, + EV_UNK, + EV_MAX = 31, +} + +pub const fn int_to_event_type(code: u32) -> Option { + match code { + 0 => Some(EventType::EV_SYN), + 1 => Some(EventType::EV_KEY), + 2 => Some(EventType::EV_REL), + 3 => Some(EventType::EV_ABS), + 4 => Some(EventType::EV_MSC), + 5 => Some(EventType::EV_SW), + 17 => Some(EventType::EV_LED), + 18 => Some(EventType::EV_SND), + 20 => Some(EventType::EV_REP), + 21 => Some(EventType::EV_FF), + 22 => Some(EventType::EV_PWR), + 23 => Some(EventType::EV_FF_STATUS), + c if c < 31 => Some(EventType::EV_UNK), + 31 => Some(EventType::EV_MAX), + _ => None, + } +} + +impl std::str::FromStr for EventType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "EV_SYN" => Ok(EventType::EV_SYN), + "EV_KEY" => Ok(EventType::EV_KEY), + "EV_REL" => Ok(EventType::EV_REL), + "EV_ABS" => Ok(EventType::EV_ABS), + "EV_MSC" => Ok(EventType::EV_MSC), + "EV_SW" => Ok(EventType::EV_SW), + "EV_LED" => Ok(EventType::EV_LED), + "EV_SND" => Ok(EventType::EV_SND), + "EV_REP" => Ok(EventType::EV_REP), + "EV_FF" => Ok(EventType::EV_FF), + "EV_PWR" => Ok(EventType::EV_PWR), + "EV_FF_STATUS" => Ok(EventType::EV_FF_STATUS), + "EV_MAX" => Ok(EventType::EV_MAX), + _ => Err(()), + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] +pub enum EventCode { + EV_SYN(EV_SYN), + EV_KEY(EV_KEY), + EV_REL(EV_REL), + EV_ABS(EV_ABS), + EV_MSC(EV_MSC), + EV_SW(EV_SW), + EV_LED(EV_LED), + EV_SND(EV_SND), + EV_REP(EV_REP), + EV_FF(EV_FF), + EV_PWR, + EV_FF_STATUS(EV_FF), + EV_UNK { event_type: u32, event_code: u32 }, + EV_MAX, +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_REL { + REL_X = 0, + REL_Y = 1, + REL_Z = 2, + REL_RX = 3, + REL_RY = 4, + REL_RZ = 5, + REL_HWHEEL = 6, + REL_DIAL = 7, + REL_WHEEL = 8, + REL_MISC = 9, + REL_RESERVED = 10, + REL_WHEEL_HI_RES = 11, + REL_HWHEEL_HI_RES = 12, + REL_MAX = 15, +} + +pub const fn int_to_ev_rel(code: u32) -> Option { + match code { + 0 => Some(EV_REL::REL_X), + 1 => Some(EV_REL::REL_Y), + 2 => Some(EV_REL::REL_Z), + 3 => Some(EV_REL::REL_RX), + 4 => Some(EV_REL::REL_RY), + 5 => Some(EV_REL::REL_RZ), + 6 => Some(EV_REL::REL_HWHEEL), + 7 => Some(EV_REL::REL_DIAL), + 8 => Some(EV_REL::REL_WHEEL), + 9 => Some(EV_REL::REL_MISC), + 10 => Some(EV_REL::REL_RESERVED), + 11 => Some(EV_REL::REL_WHEEL_HI_RES), + 12 => Some(EV_REL::REL_HWHEEL_HI_RES), + 15 => Some(EV_REL::REL_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_REL { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "REL_X" => Ok(EV_REL::REL_X), + "REL_Y" => Ok(EV_REL::REL_Y), + "REL_Z" => Ok(EV_REL::REL_Z), + "REL_RX" => Ok(EV_REL::REL_RX), + "REL_RY" => Ok(EV_REL::REL_RY), + "REL_RZ" => Ok(EV_REL::REL_RZ), + "REL_HWHEEL" => Ok(EV_REL::REL_HWHEEL), + "REL_DIAL" => Ok(EV_REL::REL_DIAL), + "REL_WHEEL" => Ok(EV_REL::REL_WHEEL), + "REL_MISC" => Ok(EV_REL::REL_MISC), + "REL_RESERVED" => Ok(EV_REL::REL_RESERVED), + "REL_WHEEL_HI_RES" => Ok(EV_REL::REL_WHEEL_HI_RES), + "REL_HWHEEL_HI_RES" => Ok(EV_REL::REL_HWHEEL_HI_RES), + "REL_MAX" => Ok(EV_REL::REL_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_REL { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_ABS { + ABS_X = 0, + ABS_Y = 1, + ABS_Z = 2, + ABS_RX = 3, + ABS_RY = 4, + ABS_RZ = 5, + ABS_THROTTLE = 6, + ABS_RUDDER = 7, + ABS_WHEEL = 8, + ABS_GAS = 9, + ABS_BRAKE = 10, + ABS_HAT0X = 16, + ABS_HAT0Y = 17, + ABS_HAT1X = 18, + ABS_HAT1Y = 19, + ABS_HAT2X = 20, + ABS_HAT2Y = 21, + ABS_HAT3X = 22, + ABS_HAT3Y = 23, + ABS_PRESSURE = 24, + ABS_DISTANCE = 25, + ABS_TILT_X = 26, + ABS_TILT_Y = 27, + ABS_TOOL_WIDTH = 28, + ABS_VOLUME = 32, + ABS_PROFILE = 33, + ABS_MISC = 40, + ABS_RESERVED = 46, + ABS_MT_SLOT = 47, + ABS_MT_TOUCH_MAJOR = 48, + ABS_MT_TOUCH_MINOR = 49, + ABS_MT_WIDTH_MAJOR = 50, + ABS_MT_WIDTH_MINOR = 51, + ABS_MT_ORIENTATION = 52, + ABS_MT_POSITION_X = 53, + ABS_MT_POSITION_Y = 54, + ABS_MT_TOOL_TYPE = 55, + ABS_MT_BLOB_ID = 56, + ABS_MT_TRACKING_ID = 57, + ABS_MT_PRESSURE = 58, + ABS_MT_DISTANCE = 59, + ABS_MT_TOOL_X = 60, + ABS_MT_TOOL_Y = 61, + ABS_MAX = 63, +} + +pub const fn int_to_ev_abs(code: u32) -> Option { + match code { + 0 => Some(EV_ABS::ABS_X), + 1 => Some(EV_ABS::ABS_Y), + 2 => Some(EV_ABS::ABS_Z), + 3 => Some(EV_ABS::ABS_RX), + 4 => Some(EV_ABS::ABS_RY), + 5 => Some(EV_ABS::ABS_RZ), + 6 => Some(EV_ABS::ABS_THROTTLE), + 7 => Some(EV_ABS::ABS_RUDDER), + 8 => Some(EV_ABS::ABS_WHEEL), + 9 => Some(EV_ABS::ABS_GAS), + 10 => Some(EV_ABS::ABS_BRAKE), + 16 => Some(EV_ABS::ABS_HAT0X), + 17 => Some(EV_ABS::ABS_HAT0Y), + 18 => Some(EV_ABS::ABS_HAT1X), + 19 => Some(EV_ABS::ABS_HAT1Y), + 20 => Some(EV_ABS::ABS_HAT2X), + 21 => Some(EV_ABS::ABS_HAT2Y), + 22 => Some(EV_ABS::ABS_HAT3X), + 23 => Some(EV_ABS::ABS_HAT3Y), + 24 => Some(EV_ABS::ABS_PRESSURE), + 25 => Some(EV_ABS::ABS_DISTANCE), + 26 => Some(EV_ABS::ABS_TILT_X), + 27 => Some(EV_ABS::ABS_TILT_Y), + 28 => Some(EV_ABS::ABS_TOOL_WIDTH), + 32 => Some(EV_ABS::ABS_VOLUME), + 33 => Some(EV_ABS::ABS_PROFILE), + 40 => Some(EV_ABS::ABS_MISC), + 46 => Some(EV_ABS::ABS_RESERVED), + 47 => Some(EV_ABS::ABS_MT_SLOT), + 48 => Some(EV_ABS::ABS_MT_TOUCH_MAJOR), + 49 => Some(EV_ABS::ABS_MT_TOUCH_MINOR), + 50 => Some(EV_ABS::ABS_MT_WIDTH_MAJOR), + 51 => Some(EV_ABS::ABS_MT_WIDTH_MINOR), + 52 => Some(EV_ABS::ABS_MT_ORIENTATION), + 53 => Some(EV_ABS::ABS_MT_POSITION_X), + 54 => Some(EV_ABS::ABS_MT_POSITION_Y), + 55 => Some(EV_ABS::ABS_MT_TOOL_TYPE), + 56 => Some(EV_ABS::ABS_MT_BLOB_ID), + 57 => Some(EV_ABS::ABS_MT_TRACKING_ID), + 58 => Some(EV_ABS::ABS_MT_PRESSURE), + 59 => Some(EV_ABS::ABS_MT_DISTANCE), + 60 => Some(EV_ABS::ABS_MT_TOOL_X), + 61 => Some(EV_ABS::ABS_MT_TOOL_Y), + 63 => Some(EV_ABS::ABS_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_ABS { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "ABS_X" => Ok(EV_ABS::ABS_X), + "ABS_Y" => Ok(EV_ABS::ABS_Y), + "ABS_Z" => Ok(EV_ABS::ABS_Z), + "ABS_RX" => Ok(EV_ABS::ABS_RX), + "ABS_RY" => Ok(EV_ABS::ABS_RY), + "ABS_RZ" => Ok(EV_ABS::ABS_RZ), + "ABS_THROTTLE" => Ok(EV_ABS::ABS_THROTTLE), + "ABS_RUDDER" => Ok(EV_ABS::ABS_RUDDER), + "ABS_WHEEL" => Ok(EV_ABS::ABS_WHEEL), + "ABS_GAS" => Ok(EV_ABS::ABS_GAS), + "ABS_BRAKE" => Ok(EV_ABS::ABS_BRAKE), + "ABS_HAT0X" => Ok(EV_ABS::ABS_HAT0X), + "ABS_HAT0Y" => Ok(EV_ABS::ABS_HAT0Y), + "ABS_HAT1X" => Ok(EV_ABS::ABS_HAT1X), + "ABS_HAT1Y" => Ok(EV_ABS::ABS_HAT1Y), + "ABS_HAT2X" => Ok(EV_ABS::ABS_HAT2X), + "ABS_HAT2Y" => Ok(EV_ABS::ABS_HAT2Y), + "ABS_HAT3X" => Ok(EV_ABS::ABS_HAT3X), + "ABS_HAT3Y" => Ok(EV_ABS::ABS_HAT3Y), + "ABS_PRESSURE" => Ok(EV_ABS::ABS_PRESSURE), + "ABS_DISTANCE" => Ok(EV_ABS::ABS_DISTANCE), + "ABS_TILT_X" => Ok(EV_ABS::ABS_TILT_X), + "ABS_TILT_Y" => Ok(EV_ABS::ABS_TILT_Y), + "ABS_TOOL_WIDTH" => Ok(EV_ABS::ABS_TOOL_WIDTH), + "ABS_VOLUME" => Ok(EV_ABS::ABS_VOLUME), + "ABS_PROFILE" => Ok(EV_ABS::ABS_PROFILE), + "ABS_MISC" => Ok(EV_ABS::ABS_MISC), + "ABS_RESERVED" => Ok(EV_ABS::ABS_RESERVED), + "ABS_MT_SLOT" => Ok(EV_ABS::ABS_MT_SLOT), + "ABS_MT_TOUCH_MAJOR" => Ok(EV_ABS::ABS_MT_TOUCH_MAJOR), + "ABS_MT_TOUCH_MINOR" => Ok(EV_ABS::ABS_MT_TOUCH_MINOR), + "ABS_MT_WIDTH_MAJOR" => Ok(EV_ABS::ABS_MT_WIDTH_MAJOR), + "ABS_MT_WIDTH_MINOR" => Ok(EV_ABS::ABS_MT_WIDTH_MINOR), + "ABS_MT_ORIENTATION" => Ok(EV_ABS::ABS_MT_ORIENTATION), + "ABS_MT_POSITION_X" => Ok(EV_ABS::ABS_MT_POSITION_X), + "ABS_MT_POSITION_Y" => Ok(EV_ABS::ABS_MT_POSITION_Y), + "ABS_MT_TOOL_TYPE" => Ok(EV_ABS::ABS_MT_TOOL_TYPE), + "ABS_MT_BLOB_ID" => Ok(EV_ABS::ABS_MT_BLOB_ID), + "ABS_MT_TRACKING_ID" => Ok(EV_ABS::ABS_MT_TRACKING_ID), + "ABS_MT_PRESSURE" => Ok(EV_ABS::ABS_MT_PRESSURE), + "ABS_MT_DISTANCE" => Ok(EV_ABS::ABS_MT_DISTANCE), + "ABS_MT_TOOL_X" => Ok(EV_ABS::ABS_MT_TOOL_X), + "ABS_MT_TOOL_Y" => Ok(EV_ABS::ABS_MT_TOOL_Y), + "ABS_MAX" => Ok(EV_ABS::ABS_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_ABS { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_KEY { + KEY_RESERVED = 0, + KEY_ESC = 1, + KEY_1 = 2, + KEY_2 = 3, + KEY_3 = 4, + KEY_4 = 5, + KEY_5 = 6, + KEY_6 = 7, + KEY_7 = 8, + KEY_8 = 9, + KEY_9 = 10, + KEY_0 = 11, + KEY_MINUS = 12, + KEY_EQUAL = 13, + KEY_BACKSPACE = 14, + KEY_TAB = 15, + KEY_Q = 16, + KEY_W = 17, + KEY_E = 18, + KEY_R = 19, + KEY_T = 20, + KEY_Y = 21, + KEY_U = 22, + KEY_I = 23, + KEY_O = 24, + KEY_P = 25, + KEY_LEFTBRACE = 26, + KEY_RIGHTBRACE = 27, + KEY_ENTER = 28, + KEY_LEFTCTRL = 29, + KEY_A = 30, + KEY_S = 31, + KEY_D = 32, + KEY_F = 33, + KEY_G = 34, + KEY_H = 35, + KEY_J = 36, + KEY_K = 37, + KEY_L = 38, + KEY_SEMICOLON = 39, + KEY_APOSTROPHE = 40, + KEY_GRAVE = 41, + KEY_LEFTSHIFT = 42, + KEY_BACKSLASH = 43, + KEY_Z = 44, + KEY_X = 45, + KEY_C = 46, + KEY_V = 47, + KEY_B = 48, + KEY_N = 49, + KEY_M = 50, + KEY_COMMA = 51, + KEY_DOT = 52, + KEY_SLASH = 53, + KEY_RIGHTSHIFT = 54, + KEY_KPASTERISK = 55, + KEY_LEFTALT = 56, + KEY_SPACE = 57, + KEY_CAPSLOCK = 58, + KEY_F1 = 59, + KEY_F2 = 60, + KEY_F3 = 61, + KEY_F4 = 62, + KEY_F5 = 63, + KEY_F6 = 64, + KEY_F7 = 65, + KEY_F8 = 66, + KEY_F9 = 67, + KEY_F10 = 68, + KEY_NUMLOCK = 69, + KEY_SCROLLLOCK = 70, + KEY_KP7 = 71, + KEY_KP8 = 72, + KEY_KP9 = 73, + KEY_KPMINUS = 74, + KEY_KP4 = 75, + KEY_KP5 = 76, + KEY_KP6 = 77, + KEY_KPPLUS = 78, + KEY_KP1 = 79, + KEY_KP2 = 80, + KEY_KP3 = 81, + KEY_KP0 = 82, + KEY_KPDOT = 83, + KEY_ZENKAKUHANKAKU = 85, + KEY_102ND = 86, + KEY_F11 = 87, + KEY_F12 = 88, + KEY_RO = 89, + KEY_KATAKANA = 90, + KEY_HIRAGANA = 91, + KEY_HENKAN = 92, + KEY_KATAKANAHIRAGANA = 93, + KEY_MUHENKAN = 94, + KEY_KPJPCOMMA = 95, + KEY_KPENTER = 96, + KEY_RIGHTCTRL = 97, + KEY_KPSLASH = 98, + KEY_SYSRQ = 99, + KEY_RIGHTALT = 100, + KEY_LINEFEED = 101, + KEY_HOME = 102, + KEY_UP = 103, + KEY_PAGEUP = 104, + KEY_LEFT = 105, + KEY_RIGHT = 106, + KEY_END = 107, + KEY_DOWN = 108, + KEY_PAGEDOWN = 109, + KEY_INSERT = 110, + KEY_DELETE = 111, + KEY_MACRO = 112, + KEY_MUTE = 113, + KEY_VOLUMEDOWN = 114, + KEY_VOLUMEUP = 115, + KEY_POWER = 116, + KEY_KPEQUAL = 117, + KEY_KPPLUSMINUS = 118, + KEY_PAUSE = 119, + KEY_SCALE = 120, + KEY_KPCOMMA = 121, + KEY_HANGEUL = 122, + KEY_HANJA = 123, + KEY_YEN = 124, + KEY_LEFTMETA = 125, + KEY_RIGHTMETA = 126, + KEY_COMPOSE = 127, + KEY_STOP = 128, + KEY_AGAIN = 129, + KEY_PROPS = 130, + KEY_UNDO = 131, + KEY_FRONT = 132, + KEY_COPY = 133, + KEY_OPEN = 134, + KEY_PASTE = 135, + KEY_FIND = 136, + KEY_CUT = 137, + KEY_HELP = 138, + KEY_MENU = 139, + KEY_CALC = 140, + KEY_SETUP = 141, + KEY_SLEEP = 142, + KEY_WAKEUP = 143, + KEY_FILE = 144, + KEY_SENDFILE = 145, + KEY_DELETEFILE = 146, + KEY_XFER = 147, + KEY_PROG1 = 148, + KEY_PROG2 = 149, + KEY_WWW = 150, + KEY_MSDOS = 151, + KEY_COFFEE = 152, + KEY_ROTATE_DISPLAY = 153, + KEY_CYCLEWINDOWS = 154, + KEY_MAIL = 155, + KEY_BOOKMARKS = 156, + KEY_COMPUTER = 157, + KEY_BACK = 158, + KEY_FORWARD = 159, + KEY_CLOSECD = 160, + KEY_EJECTCD = 161, + KEY_EJECTCLOSECD = 162, + KEY_NEXTSONG = 163, + KEY_PLAYPAUSE = 164, + KEY_PREVIOUSSONG = 165, + KEY_STOPCD = 166, + KEY_RECORD = 167, + KEY_REWIND = 168, + KEY_PHONE = 169, + KEY_ISO = 170, + KEY_CONFIG = 171, + KEY_HOMEPAGE = 172, + KEY_REFRESH = 173, + KEY_EXIT = 174, + KEY_MOVE = 175, + KEY_EDIT = 176, + KEY_SCROLLUP = 177, + KEY_SCROLLDOWN = 178, + KEY_KPLEFTPAREN = 179, + KEY_KPRIGHTPAREN = 180, + KEY_NEW = 181, + KEY_REDO = 182, + KEY_F13 = 183, + KEY_F14 = 184, + KEY_F15 = 185, + KEY_F16 = 186, + KEY_F17 = 187, + KEY_F18 = 188, + KEY_F19 = 189, + KEY_F20 = 190, + KEY_F21 = 191, + KEY_F22 = 192, + KEY_F23 = 193, + KEY_F24 = 194, + KEY_PLAYCD = 200, + KEY_PAUSECD = 201, + KEY_PROG3 = 202, + KEY_PROG4 = 203, + KEY_ALL_APPLICATIONS = 204, + KEY_SUSPEND = 205, + KEY_CLOSE = 206, + KEY_PLAY = 207, + KEY_FASTFORWARD = 208, + KEY_BASSBOOST = 209, + KEY_PRINT = 210, + KEY_HP = 211, + KEY_CAMERA = 212, + KEY_SOUND = 213, + KEY_QUESTION = 214, + KEY_EMAIL = 215, + KEY_CHAT = 216, + KEY_SEARCH = 217, + KEY_CONNECT = 218, + KEY_FINANCE = 219, + KEY_SPORT = 220, + KEY_SHOP = 221, + KEY_ALTERASE = 222, + KEY_CANCEL = 223, + KEY_BRIGHTNESSDOWN = 224, + KEY_BRIGHTNESSUP = 225, + KEY_MEDIA = 226, + KEY_SWITCHVIDEOMODE = 227, + KEY_KBDILLUMTOGGLE = 228, + KEY_KBDILLUMDOWN = 229, + KEY_KBDILLUMUP = 230, + KEY_SEND = 231, + KEY_REPLY = 232, + KEY_FORWARDMAIL = 233, + KEY_SAVE = 234, + KEY_DOCUMENTS = 235, + KEY_BATTERY = 236, + KEY_BLUETOOTH = 237, + KEY_WLAN = 238, + KEY_UWB = 239, + KEY_UNKNOWN = 240, + KEY_VIDEO_NEXT = 241, + KEY_VIDEO_PREV = 242, + KEY_BRIGHTNESS_CYCLE = 243, + KEY_BRIGHTNESS_AUTO = 244, + KEY_DISPLAY_OFF = 245, + KEY_WWAN = 246, + KEY_RFKILL = 247, + KEY_MICMUTE = 248, + KEY_OK = 352, + KEY_SELECT = 353, + KEY_GOTO = 354, + KEY_CLEAR = 355, + KEY_POWER2 = 356, + KEY_OPTION = 357, + KEY_INFO = 358, + KEY_TIME = 359, + KEY_VENDOR = 360, + KEY_ARCHIVE = 361, + KEY_PROGRAM = 362, + KEY_CHANNEL = 363, + KEY_FAVORITES = 364, + KEY_EPG = 365, + KEY_PVR = 366, + KEY_MHP = 367, + KEY_LANGUAGE = 368, + KEY_TITLE = 369, + KEY_SUBTITLE = 370, + KEY_ANGLE = 371, + KEY_FULL_SCREEN = 372, + KEY_MODE = 373, + KEY_KEYBOARD = 374, + KEY_ASPECT_RATIO = 375, + KEY_PC = 376, + KEY_TV = 377, + KEY_TV2 = 378, + KEY_VCR = 379, + KEY_VCR2 = 380, + KEY_SAT = 381, + KEY_SAT2 = 382, + KEY_CD = 383, + KEY_TAPE = 384, + KEY_RADIO = 385, + KEY_TUNER = 386, + KEY_PLAYER = 387, + KEY_TEXT = 388, + KEY_DVD = 389, + KEY_AUX = 390, + KEY_MP3 = 391, + KEY_AUDIO = 392, + KEY_VIDEO = 393, + KEY_DIRECTORY = 394, + KEY_LIST = 395, + KEY_MEMO = 396, + KEY_CALENDAR = 397, + KEY_RED = 398, + KEY_GREEN = 399, + KEY_YELLOW = 400, + KEY_BLUE = 401, + KEY_CHANNELUP = 402, + KEY_CHANNELDOWN = 403, + KEY_FIRST = 404, + KEY_LAST = 405, + KEY_AB = 406, + KEY_NEXT = 407, + KEY_RESTART = 408, + KEY_SLOW = 409, + KEY_SHUFFLE = 410, + KEY_BREAK = 411, + KEY_PREVIOUS = 412, + KEY_DIGITS = 413, + KEY_TEEN = 414, + KEY_TWEN = 415, + KEY_VIDEOPHONE = 416, + KEY_GAMES = 417, + KEY_ZOOMIN = 418, + KEY_ZOOMOUT = 419, + KEY_ZOOMRESET = 420, + KEY_WORDPROCESSOR = 421, + KEY_EDITOR = 422, + KEY_SPREADSHEET = 423, + KEY_GRAPHICSEDITOR = 424, + KEY_PRESENTATION = 425, + KEY_DATABASE = 426, + KEY_NEWS = 427, + KEY_VOICEMAIL = 428, + KEY_ADDRESSBOOK = 429, + KEY_MESSENGER = 430, + KEY_DISPLAYTOGGLE = 431, + KEY_SPELLCHECK = 432, + KEY_LOGOFF = 433, + KEY_DOLLAR = 434, + KEY_EURO = 435, + KEY_FRAMEBACK = 436, + KEY_FRAMEFORWARD = 437, + KEY_CONTEXT_MENU = 438, + KEY_MEDIA_REPEAT = 439, + KEY_10CHANNELSUP = 440, + KEY_10CHANNELSDOWN = 441, + KEY_IMAGES = 442, + KEY_NOTIFICATION_CENTER = 444, + KEY_PICKUP_PHONE = 445, + KEY_HANGUP_PHONE = 446, + KEY_DEL_EOL = 448, + KEY_DEL_EOS = 449, + KEY_INS_LINE = 450, + KEY_DEL_LINE = 451, + KEY_FN = 464, + KEY_FN_ESC = 465, + KEY_FN_F1 = 466, + KEY_FN_F2 = 467, + KEY_FN_F3 = 468, + KEY_FN_F4 = 469, + KEY_FN_F5 = 470, + KEY_FN_F6 = 471, + KEY_FN_F7 = 472, + KEY_FN_F8 = 473, + KEY_FN_F9 = 474, + KEY_FN_F10 = 475, + KEY_FN_F11 = 476, + KEY_FN_F12 = 477, + KEY_FN_1 = 478, + KEY_FN_2 = 479, + KEY_FN_D = 480, + KEY_FN_E = 481, + KEY_FN_F = 482, + KEY_FN_S = 483, + KEY_FN_B = 484, + KEY_FN_RIGHT_SHIFT = 485, + KEY_BRL_DOT1 = 497, + KEY_BRL_DOT2 = 498, + KEY_BRL_DOT3 = 499, + KEY_BRL_DOT4 = 500, + KEY_BRL_DOT5 = 501, + KEY_BRL_DOT6 = 502, + KEY_BRL_DOT7 = 503, + KEY_BRL_DOT8 = 504, + KEY_BRL_DOT9 = 505, + KEY_BRL_DOT10 = 506, + KEY_NUMERIC_0 = 512, + KEY_NUMERIC_1 = 513, + KEY_NUMERIC_2 = 514, + KEY_NUMERIC_3 = 515, + KEY_NUMERIC_4 = 516, + KEY_NUMERIC_5 = 517, + KEY_NUMERIC_6 = 518, + KEY_NUMERIC_7 = 519, + KEY_NUMERIC_8 = 520, + KEY_NUMERIC_9 = 521, + KEY_NUMERIC_STAR = 522, + KEY_NUMERIC_POUND = 523, + KEY_NUMERIC_A = 524, + KEY_NUMERIC_B = 525, + KEY_NUMERIC_C = 526, + KEY_NUMERIC_D = 527, + KEY_CAMERA_FOCUS = 528, + KEY_WPS_BUTTON = 529, + KEY_TOUCHPAD_TOGGLE = 530, + KEY_TOUCHPAD_ON = 531, + KEY_TOUCHPAD_OFF = 532, + KEY_CAMERA_ZOOMIN = 533, + KEY_CAMERA_ZOOMOUT = 534, + KEY_CAMERA_UP = 535, + KEY_CAMERA_DOWN = 536, + KEY_CAMERA_LEFT = 537, + KEY_CAMERA_RIGHT = 538, + KEY_ATTENDANT_ON = 539, + KEY_ATTENDANT_OFF = 540, + KEY_ATTENDANT_TOGGLE = 541, + KEY_LIGHTS_TOGGLE = 542, + KEY_ALS_TOGGLE = 560, + KEY_ROTATE_LOCK_TOGGLE = 561, + KEY_REFRESH_RATE_TOGGLE = 562, + KEY_BUTTONCONFIG = 576, + KEY_TASKMANAGER = 577, + KEY_JOURNAL = 578, + KEY_CONTROLPANEL = 579, + KEY_APPSELECT = 580, + KEY_SCREENSAVER = 581, + KEY_VOICECOMMAND = 582, + KEY_ASSISTANT = 583, + KEY_KBD_LAYOUT_NEXT = 584, + KEY_EMOJI_PICKER = 585, + KEY_DICTATE = 586, + KEY_CAMERA_ACCESS_ENABLE = 587, + KEY_CAMERA_ACCESS_DISABLE = 588, + KEY_CAMERA_ACCESS_TOGGLE = 589, + KEY_ACCESSIBILITY = 590, + KEY_DO_NOT_DISTURB = 591, + KEY_BRIGHTNESS_MIN = 592, + KEY_BRIGHTNESS_MAX = 593, + KEY_KBDINPUTASSIST_PREV = 608, + KEY_KBDINPUTASSIST_NEXT = 609, + KEY_KBDINPUTASSIST_PREVGROUP = 610, + KEY_KBDINPUTASSIST_NEXTGROUP = 611, + KEY_KBDINPUTASSIST_ACCEPT = 612, + KEY_KBDINPUTASSIST_CANCEL = 613, + KEY_RIGHT_UP = 614, + KEY_RIGHT_DOWN = 615, + KEY_LEFT_UP = 616, + KEY_LEFT_DOWN = 617, + KEY_ROOT_MENU = 618, + KEY_MEDIA_TOP_MENU = 619, + KEY_NUMERIC_11 = 620, + KEY_NUMERIC_12 = 621, + KEY_AUDIO_DESC = 622, + KEY_3D_MODE = 623, + KEY_NEXT_FAVORITE = 624, + KEY_STOP_RECORD = 625, + KEY_PAUSE_RECORD = 626, + KEY_VOD = 627, + KEY_UNMUTE = 628, + KEY_FASTREVERSE = 629, + KEY_SLOWREVERSE = 630, + KEY_DATA = 631, + KEY_ONSCREEN_KEYBOARD = 632, + KEY_PRIVACY_SCREEN_TOGGLE = 633, + KEY_SELECTIVE_SCREENSHOT = 634, + KEY_NEXT_ELEMENT = 635, + KEY_PREVIOUS_ELEMENT = 636, + KEY_AUTOPILOT_ENGAGE_TOGGLE = 637, + KEY_MARK_WAYPOINT = 638, + KEY_SOS = 639, + KEY_NAV_CHART = 640, + KEY_FISHING_CHART = 641, + KEY_SINGLE_RANGE_RADAR = 642, + KEY_DUAL_RANGE_RADAR = 643, + KEY_RADAR_OVERLAY = 644, + KEY_TRADITIONAL_SONAR = 645, + KEY_CLEARVU_SONAR = 646, + KEY_SIDEVU_SONAR = 647, + KEY_NAV_INFO = 648, + KEY_BRIGHTNESS_MENU = 649, + KEY_MACRO1 = 656, + KEY_MACRO2 = 657, + KEY_MACRO3 = 658, + KEY_MACRO4 = 659, + KEY_MACRO5 = 660, + KEY_MACRO6 = 661, + KEY_MACRO7 = 662, + KEY_MACRO8 = 663, + KEY_MACRO9 = 664, + KEY_MACRO10 = 665, + KEY_MACRO11 = 666, + KEY_MACRO12 = 667, + KEY_MACRO13 = 668, + KEY_MACRO14 = 669, + KEY_MACRO15 = 670, + KEY_MACRO16 = 671, + KEY_MACRO17 = 672, + KEY_MACRO18 = 673, + KEY_MACRO19 = 674, + KEY_MACRO20 = 675, + KEY_MACRO21 = 676, + KEY_MACRO22 = 677, + KEY_MACRO23 = 678, + KEY_MACRO24 = 679, + KEY_MACRO25 = 680, + KEY_MACRO26 = 681, + KEY_MACRO27 = 682, + KEY_MACRO28 = 683, + KEY_MACRO29 = 684, + KEY_MACRO30 = 685, + KEY_MACRO_RECORD_START = 688, + KEY_MACRO_RECORD_STOP = 689, + KEY_MACRO_PRESET_CYCLE = 690, + KEY_MACRO_PRESET1 = 691, + KEY_MACRO_PRESET2 = 692, + KEY_MACRO_PRESET3 = 693, + KEY_KBD_LCD_MENU1 = 696, + KEY_KBD_LCD_MENU2 = 697, + KEY_KBD_LCD_MENU3 = 698, + KEY_KBD_LCD_MENU4 = 699, + KEY_KBD_LCD_MENU5 = 700, + KEY_MAX = 767, + BTN_0 = 256, + BTN_1 = 257, + BTN_2 = 258, + BTN_3 = 259, + BTN_4 = 260, + BTN_5 = 261, + BTN_6 = 262, + BTN_7 = 263, + BTN_8 = 264, + BTN_9 = 265, + BTN_LEFT = 272, + BTN_RIGHT = 273, + BTN_MIDDLE = 274, + BTN_SIDE = 275, + BTN_EXTRA = 276, + BTN_FORWARD = 277, + BTN_BACK = 278, + BTN_TASK = 279, + BTN_TRIGGER = 288, + BTN_THUMB = 289, + BTN_THUMB2 = 290, + BTN_TOP = 291, + BTN_TOP2 = 292, + BTN_PINKIE = 293, + BTN_BASE = 294, + BTN_BASE2 = 295, + BTN_BASE3 = 296, + BTN_BASE4 = 297, + BTN_BASE5 = 298, + BTN_BASE6 = 299, + BTN_DEAD = 303, + BTN_SOUTH = 304, + BTN_EAST = 305, + BTN_C = 306, + BTN_NORTH = 307, + BTN_WEST = 308, + BTN_Z = 309, + BTN_TL = 310, + BTN_TR = 311, + BTN_TL2 = 312, + BTN_TR2 = 313, + BTN_SELECT = 314, + BTN_START = 315, + BTN_MODE = 316, + BTN_THUMBL = 317, + BTN_THUMBR = 318, + BTN_TOOL_PEN = 320, + BTN_TOOL_RUBBER = 321, + BTN_TOOL_BRUSH = 322, + BTN_TOOL_PENCIL = 323, + BTN_TOOL_AIRBRUSH = 324, + BTN_TOOL_FINGER = 325, + BTN_TOOL_MOUSE = 326, + BTN_TOOL_LENS = 327, + BTN_TOOL_QUINTTAP = 328, + BTN_STYLUS3 = 329, + BTN_TOUCH = 330, + BTN_STYLUS = 331, + BTN_STYLUS2 = 332, + BTN_TOOL_DOUBLETAP = 333, + BTN_TOOL_TRIPLETAP = 334, + BTN_TOOL_QUADTAP = 335, + BTN_GEAR_DOWN = 336, + BTN_GEAR_UP = 337, + BTN_DPAD_UP = 544, + BTN_DPAD_DOWN = 545, + BTN_DPAD_LEFT = 546, + BTN_DPAD_RIGHT = 547, + BTN_TRIGGER_HAPPY1 = 704, + BTN_TRIGGER_HAPPY2 = 705, + BTN_TRIGGER_HAPPY3 = 706, + BTN_TRIGGER_HAPPY4 = 707, + BTN_TRIGGER_HAPPY5 = 708, + BTN_TRIGGER_HAPPY6 = 709, + BTN_TRIGGER_HAPPY7 = 710, + BTN_TRIGGER_HAPPY8 = 711, + BTN_TRIGGER_HAPPY9 = 712, + BTN_TRIGGER_HAPPY10 = 713, + BTN_TRIGGER_HAPPY11 = 714, + BTN_TRIGGER_HAPPY12 = 715, + BTN_TRIGGER_HAPPY13 = 716, + BTN_TRIGGER_HAPPY14 = 717, + BTN_TRIGGER_HAPPY15 = 718, + BTN_TRIGGER_HAPPY16 = 719, + BTN_TRIGGER_HAPPY17 = 720, + BTN_TRIGGER_HAPPY18 = 721, + BTN_TRIGGER_HAPPY19 = 722, + BTN_TRIGGER_HAPPY20 = 723, + BTN_TRIGGER_HAPPY21 = 724, + BTN_TRIGGER_HAPPY22 = 725, + BTN_TRIGGER_HAPPY23 = 726, + BTN_TRIGGER_HAPPY24 = 727, + BTN_TRIGGER_HAPPY25 = 728, + BTN_TRIGGER_HAPPY26 = 729, + BTN_TRIGGER_HAPPY27 = 730, + BTN_TRIGGER_HAPPY28 = 731, + BTN_TRIGGER_HAPPY29 = 732, + BTN_TRIGGER_HAPPY30 = 733, + BTN_TRIGGER_HAPPY31 = 734, + BTN_TRIGGER_HAPPY32 = 735, + BTN_TRIGGER_HAPPY33 = 736, + BTN_TRIGGER_HAPPY34 = 737, + BTN_TRIGGER_HAPPY35 = 738, + BTN_TRIGGER_HAPPY36 = 739, + BTN_TRIGGER_HAPPY37 = 740, + BTN_TRIGGER_HAPPY38 = 741, + BTN_TRIGGER_HAPPY39 = 742, + BTN_TRIGGER_HAPPY40 = 743, +} + +pub const fn int_to_ev_key(code: u32) -> Option { + match code { + 0 => Some(EV_KEY::KEY_RESERVED), + 1 => Some(EV_KEY::KEY_ESC), + 2 => Some(EV_KEY::KEY_1), + 3 => Some(EV_KEY::KEY_2), + 4 => Some(EV_KEY::KEY_3), + 5 => Some(EV_KEY::KEY_4), + 6 => Some(EV_KEY::KEY_5), + 7 => Some(EV_KEY::KEY_6), + 8 => Some(EV_KEY::KEY_7), + 9 => Some(EV_KEY::KEY_8), + 10 => Some(EV_KEY::KEY_9), + 11 => Some(EV_KEY::KEY_0), + 12 => Some(EV_KEY::KEY_MINUS), + 13 => Some(EV_KEY::KEY_EQUAL), + 14 => Some(EV_KEY::KEY_BACKSPACE), + 15 => Some(EV_KEY::KEY_TAB), + 16 => Some(EV_KEY::KEY_Q), + 17 => Some(EV_KEY::KEY_W), + 18 => Some(EV_KEY::KEY_E), + 19 => Some(EV_KEY::KEY_R), + 20 => Some(EV_KEY::KEY_T), + 21 => Some(EV_KEY::KEY_Y), + 22 => Some(EV_KEY::KEY_U), + 23 => Some(EV_KEY::KEY_I), + 24 => Some(EV_KEY::KEY_O), + 25 => Some(EV_KEY::KEY_P), + 26 => Some(EV_KEY::KEY_LEFTBRACE), + 27 => Some(EV_KEY::KEY_RIGHTBRACE), + 28 => Some(EV_KEY::KEY_ENTER), + 29 => Some(EV_KEY::KEY_LEFTCTRL), + 30 => Some(EV_KEY::KEY_A), + 31 => Some(EV_KEY::KEY_S), + 32 => Some(EV_KEY::KEY_D), + 33 => Some(EV_KEY::KEY_F), + 34 => Some(EV_KEY::KEY_G), + 35 => Some(EV_KEY::KEY_H), + 36 => Some(EV_KEY::KEY_J), + 37 => Some(EV_KEY::KEY_K), + 38 => Some(EV_KEY::KEY_L), + 39 => Some(EV_KEY::KEY_SEMICOLON), + 40 => Some(EV_KEY::KEY_APOSTROPHE), + 41 => Some(EV_KEY::KEY_GRAVE), + 42 => Some(EV_KEY::KEY_LEFTSHIFT), + 43 => Some(EV_KEY::KEY_BACKSLASH), + 44 => Some(EV_KEY::KEY_Z), + 45 => Some(EV_KEY::KEY_X), + 46 => Some(EV_KEY::KEY_C), + 47 => Some(EV_KEY::KEY_V), + 48 => Some(EV_KEY::KEY_B), + 49 => Some(EV_KEY::KEY_N), + 50 => Some(EV_KEY::KEY_M), + 51 => Some(EV_KEY::KEY_COMMA), + 52 => Some(EV_KEY::KEY_DOT), + 53 => Some(EV_KEY::KEY_SLASH), + 54 => Some(EV_KEY::KEY_RIGHTSHIFT), + 55 => Some(EV_KEY::KEY_KPASTERISK), + 56 => Some(EV_KEY::KEY_LEFTALT), + 57 => Some(EV_KEY::KEY_SPACE), + 58 => Some(EV_KEY::KEY_CAPSLOCK), + 59 => Some(EV_KEY::KEY_F1), + 60 => Some(EV_KEY::KEY_F2), + 61 => Some(EV_KEY::KEY_F3), + 62 => Some(EV_KEY::KEY_F4), + 63 => Some(EV_KEY::KEY_F5), + 64 => Some(EV_KEY::KEY_F6), + 65 => Some(EV_KEY::KEY_F7), + 66 => Some(EV_KEY::KEY_F8), + 67 => Some(EV_KEY::KEY_F9), + 68 => Some(EV_KEY::KEY_F10), + 69 => Some(EV_KEY::KEY_NUMLOCK), + 70 => Some(EV_KEY::KEY_SCROLLLOCK), + 71 => Some(EV_KEY::KEY_KP7), + 72 => Some(EV_KEY::KEY_KP8), + 73 => Some(EV_KEY::KEY_KP9), + 74 => Some(EV_KEY::KEY_KPMINUS), + 75 => Some(EV_KEY::KEY_KP4), + 76 => Some(EV_KEY::KEY_KP5), + 77 => Some(EV_KEY::KEY_KP6), + 78 => Some(EV_KEY::KEY_KPPLUS), + 79 => Some(EV_KEY::KEY_KP1), + 80 => Some(EV_KEY::KEY_KP2), + 81 => Some(EV_KEY::KEY_KP3), + 82 => Some(EV_KEY::KEY_KP0), + 83 => Some(EV_KEY::KEY_KPDOT), + 85 => Some(EV_KEY::KEY_ZENKAKUHANKAKU), + 86 => Some(EV_KEY::KEY_102ND), + 87 => Some(EV_KEY::KEY_F11), + 88 => Some(EV_KEY::KEY_F12), + 89 => Some(EV_KEY::KEY_RO), + 90 => Some(EV_KEY::KEY_KATAKANA), + 91 => Some(EV_KEY::KEY_HIRAGANA), + 92 => Some(EV_KEY::KEY_HENKAN), + 93 => Some(EV_KEY::KEY_KATAKANAHIRAGANA), + 94 => Some(EV_KEY::KEY_MUHENKAN), + 95 => Some(EV_KEY::KEY_KPJPCOMMA), + 96 => Some(EV_KEY::KEY_KPENTER), + 97 => Some(EV_KEY::KEY_RIGHTCTRL), + 98 => Some(EV_KEY::KEY_KPSLASH), + 99 => Some(EV_KEY::KEY_SYSRQ), + 100 => Some(EV_KEY::KEY_RIGHTALT), + 101 => Some(EV_KEY::KEY_LINEFEED), + 102 => Some(EV_KEY::KEY_HOME), + 103 => Some(EV_KEY::KEY_UP), + 104 => Some(EV_KEY::KEY_PAGEUP), + 105 => Some(EV_KEY::KEY_LEFT), + 106 => Some(EV_KEY::KEY_RIGHT), + 107 => Some(EV_KEY::KEY_END), + 108 => Some(EV_KEY::KEY_DOWN), + 109 => Some(EV_KEY::KEY_PAGEDOWN), + 110 => Some(EV_KEY::KEY_INSERT), + 111 => Some(EV_KEY::KEY_DELETE), + 112 => Some(EV_KEY::KEY_MACRO), + 113 => Some(EV_KEY::KEY_MUTE), + 114 => Some(EV_KEY::KEY_VOLUMEDOWN), + 115 => Some(EV_KEY::KEY_VOLUMEUP), + 116 => Some(EV_KEY::KEY_POWER), + 117 => Some(EV_KEY::KEY_KPEQUAL), + 118 => Some(EV_KEY::KEY_KPPLUSMINUS), + 119 => Some(EV_KEY::KEY_PAUSE), + 120 => Some(EV_KEY::KEY_SCALE), + 121 => Some(EV_KEY::KEY_KPCOMMA), + 122 => Some(EV_KEY::KEY_HANGEUL), + 123 => Some(EV_KEY::KEY_HANJA), + 124 => Some(EV_KEY::KEY_YEN), + 125 => Some(EV_KEY::KEY_LEFTMETA), + 126 => Some(EV_KEY::KEY_RIGHTMETA), + 127 => Some(EV_KEY::KEY_COMPOSE), + 128 => Some(EV_KEY::KEY_STOP), + 129 => Some(EV_KEY::KEY_AGAIN), + 130 => Some(EV_KEY::KEY_PROPS), + 131 => Some(EV_KEY::KEY_UNDO), + 132 => Some(EV_KEY::KEY_FRONT), + 133 => Some(EV_KEY::KEY_COPY), + 134 => Some(EV_KEY::KEY_OPEN), + 135 => Some(EV_KEY::KEY_PASTE), + 136 => Some(EV_KEY::KEY_FIND), + 137 => Some(EV_KEY::KEY_CUT), + 138 => Some(EV_KEY::KEY_HELP), + 139 => Some(EV_KEY::KEY_MENU), + 140 => Some(EV_KEY::KEY_CALC), + 141 => Some(EV_KEY::KEY_SETUP), + 142 => Some(EV_KEY::KEY_SLEEP), + 143 => Some(EV_KEY::KEY_WAKEUP), + 144 => Some(EV_KEY::KEY_FILE), + 145 => Some(EV_KEY::KEY_SENDFILE), + 146 => Some(EV_KEY::KEY_DELETEFILE), + 147 => Some(EV_KEY::KEY_XFER), + 148 => Some(EV_KEY::KEY_PROG1), + 149 => Some(EV_KEY::KEY_PROG2), + 150 => Some(EV_KEY::KEY_WWW), + 151 => Some(EV_KEY::KEY_MSDOS), + 152 => Some(EV_KEY::KEY_COFFEE), + 153 => Some(EV_KEY::KEY_ROTATE_DISPLAY), + 154 => Some(EV_KEY::KEY_CYCLEWINDOWS), + 155 => Some(EV_KEY::KEY_MAIL), + 156 => Some(EV_KEY::KEY_BOOKMARKS), + 157 => Some(EV_KEY::KEY_COMPUTER), + 158 => Some(EV_KEY::KEY_BACK), + 159 => Some(EV_KEY::KEY_FORWARD), + 160 => Some(EV_KEY::KEY_CLOSECD), + 161 => Some(EV_KEY::KEY_EJECTCD), + 162 => Some(EV_KEY::KEY_EJECTCLOSECD), + 163 => Some(EV_KEY::KEY_NEXTSONG), + 164 => Some(EV_KEY::KEY_PLAYPAUSE), + 165 => Some(EV_KEY::KEY_PREVIOUSSONG), + 166 => Some(EV_KEY::KEY_STOPCD), + 167 => Some(EV_KEY::KEY_RECORD), + 168 => Some(EV_KEY::KEY_REWIND), + 169 => Some(EV_KEY::KEY_PHONE), + 170 => Some(EV_KEY::KEY_ISO), + 171 => Some(EV_KEY::KEY_CONFIG), + 172 => Some(EV_KEY::KEY_HOMEPAGE), + 173 => Some(EV_KEY::KEY_REFRESH), + 174 => Some(EV_KEY::KEY_EXIT), + 175 => Some(EV_KEY::KEY_MOVE), + 176 => Some(EV_KEY::KEY_EDIT), + 177 => Some(EV_KEY::KEY_SCROLLUP), + 178 => Some(EV_KEY::KEY_SCROLLDOWN), + 179 => Some(EV_KEY::KEY_KPLEFTPAREN), + 180 => Some(EV_KEY::KEY_KPRIGHTPAREN), + 181 => Some(EV_KEY::KEY_NEW), + 182 => Some(EV_KEY::KEY_REDO), + 183 => Some(EV_KEY::KEY_F13), + 184 => Some(EV_KEY::KEY_F14), + 185 => Some(EV_KEY::KEY_F15), + 186 => Some(EV_KEY::KEY_F16), + 187 => Some(EV_KEY::KEY_F17), + 188 => Some(EV_KEY::KEY_F18), + 189 => Some(EV_KEY::KEY_F19), + 190 => Some(EV_KEY::KEY_F20), + 191 => Some(EV_KEY::KEY_F21), + 192 => Some(EV_KEY::KEY_F22), + 193 => Some(EV_KEY::KEY_F23), + 194 => Some(EV_KEY::KEY_F24), + 200 => Some(EV_KEY::KEY_PLAYCD), + 201 => Some(EV_KEY::KEY_PAUSECD), + 202 => Some(EV_KEY::KEY_PROG3), + 203 => Some(EV_KEY::KEY_PROG4), + 204 => Some(EV_KEY::KEY_ALL_APPLICATIONS), + 205 => Some(EV_KEY::KEY_SUSPEND), + 206 => Some(EV_KEY::KEY_CLOSE), + 207 => Some(EV_KEY::KEY_PLAY), + 208 => Some(EV_KEY::KEY_FASTFORWARD), + 209 => Some(EV_KEY::KEY_BASSBOOST), + 210 => Some(EV_KEY::KEY_PRINT), + 211 => Some(EV_KEY::KEY_HP), + 212 => Some(EV_KEY::KEY_CAMERA), + 213 => Some(EV_KEY::KEY_SOUND), + 214 => Some(EV_KEY::KEY_QUESTION), + 215 => Some(EV_KEY::KEY_EMAIL), + 216 => Some(EV_KEY::KEY_CHAT), + 217 => Some(EV_KEY::KEY_SEARCH), + 218 => Some(EV_KEY::KEY_CONNECT), + 219 => Some(EV_KEY::KEY_FINANCE), + 220 => Some(EV_KEY::KEY_SPORT), + 221 => Some(EV_KEY::KEY_SHOP), + 222 => Some(EV_KEY::KEY_ALTERASE), + 223 => Some(EV_KEY::KEY_CANCEL), + 224 => Some(EV_KEY::KEY_BRIGHTNESSDOWN), + 225 => Some(EV_KEY::KEY_BRIGHTNESSUP), + 226 => Some(EV_KEY::KEY_MEDIA), + 227 => Some(EV_KEY::KEY_SWITCHVIDEOMODE), + 228 => Some(EV_KEY::KEY_KBDILLUMTOGGLE), + 229 => Some(EV_KEY::KEY_KBDILLUMDOWN), + 230 => Some(EV_KEY::KEY_KBDILLUMUP), + 231 => Some(EV_KEY::KEY_SEND), + 232 => Some(EV_KEY::KEY_REPLY), + 233 => Some(EV_KEY::KEY_FORWARDMAIL), + 234 => Some(EV_KEY::KEY_SAVE), + 235 => Some(EV_KEY::KEY_DOCUMENTS), + 236 => Some(EV_KEY::KEY_BATTERY), + 237 => Some(EV_KEY::KEY_BLUETOOTH), + 238 => Some(EV_KEY::KEY_WLAN), + 239 => Some(EV_KEY::KEY_UWB), + 240 => Some(EV_KEY::KEY_UNKNOWN), + 241 => Some(EV_KEY::KEY_VIDEO_NEXT), + 242 => Some(EV_KEY::KEY_VIDEO_PREV), + 243 => Some(EV_KEY::KEY_BRIGHTNESS_CYCLE), + 244 => Some(EV_KEY::KEY_BRIGHTNESS_AUTO), + 245 => Some(EV_KEY::KEY_DISPLAY_OFF), + 246 => Some(EV_KEY::KEY_WWAN), + 247 => Some(EV_KEY::KEY_RFKILL), + 248 => Some(EV_KEY::KEY_MICMUTE), + 352 => Some(EV_KEY::KEY_OK), + 353 => Some(EV_KEY::KEY_SELECT), + 354 => Some(EV_KEY::KEY_GOTO), + 355 => Some(EV_KEY::KEY_CLEAR), + 356 => Some(EV_KEY::KEY_POWER2), + 357 => Some(EV_KEY::KEY_OPTION), + 358 => Some(EV_KEY::KEY_INFO), + 359 => Some(EV_KEY::KEY_TIME), + 360 => Some(EV_KEY::KEY_VENDOR), + 361 => Some(EV_KEY::KEY_ARCHIVE), + 362 => Some(EV_KEY::KEY_PROGRAM), + 363 => Some(EV_KEY::KEY_CHANNEL), + 364 => Some(EV_KEY::KEY_FAVORITES), + 365 => Some(EV_KEY::KEY_EPG), + 366 => Some(EV_KEY::KEY_PVR), + 367 => Some(EV_KEY::KEY_MHP), + 368 => Some(EV_KEY::KEY_LANGUAGE), + 369 => Some(EV_KEY::KEY_TITLE), + 370 => Some(EV_KEY::KEY_SUBTITLE), + 371 => Some(EV_KEY::KEY_ANGLE), + 372 => Some(EV_KEY::KEY_FULL_SCREEN), + 373 => Some(EV_KEY::KEY_MODE), + 374 => Some(EV_KEY::KEY_KEYBOARD), + 375 => Some(EV_KEY::KEY_ASPECT_RATIO), + 376 => Some(EV_KEY::KEY_PC), + 377 => Some(EV_KEY::KEY_TV), + 378 => Some(EV_KEY::KEY_TV2), + 379 => Some(EV_KEY::KEY_VCR), + 380 => Some(EV_KEY::KEY_VCR2), + 381 => Some(EV_KEY::KEY_SAT), + 382 => Some(EV_KEY::KEY_SAT2), + 383 => Some(EV_KEY::KEY_CD), + 384 => Some(EV_KEY::KEY_TAPE), + 385 => Some(EV_KEY::KEY_RADIO), + 386 => Some(EV_KEY::KEY_TUNER), + 387 => Some(EV_KEY::KEY_PLAYER), + 388 => Some(EV_KEY::KEY_TEXT), + 389 => Some(EV_KEY::KEY_DVD), + 390 => Some(EV_KEY::KEY_AUX), + 391 => Some(EV_KEY::KEY_MP3), + 392 => Some(EV_KEY::KEY_AUDIO), + 393 => Some(EV_KEY::KEY_VIDEO), + 394 => Some(EV_KEY::KEY_DIRECTORY), + 395 => Some(EV_KEY::KEY_LIST), + 396 => Some(EV_KEY::KEY_MEMO), + 397 => Some(EV_KEY::KEY_CALENDAR), + 398 => Some(EV_KEY::KEY_RED), + 399 => Some(EV_KEY::KEY_GREEN), + 400 => Some(EV_KEY::KEY_YELLOW), + 401 => Some(EV_KEY::KEY_BLUE), + 402 => Some(EV_KEY::KEY_CHANNELUP), + 403 => Some(EV_KEY::KEY_CHANNELDOWN), + 404 => Some(EV_KEY::KEY_FIRST), + 405 => Some(EV_KEY::KEY_LAST), + 406 => Some(EV_KEY::KEY_AB), + 407 => Some(EV_KEY::KEY_NEXT), + 408 => Some(EV_KEY::KEY_RESTART), + 409 => Some(EV_KEY::KEY_SLOW), + 410 => Some(EV_KEY::KEY_SHUFFLE), + 411 => Some(EV_KEY::KEY_BREAK), + 412 => Some(EV_KEY::KEY_PREVIOUS), + 413 => Some(EV_KEY::KEY_DIGITS), + 414 => Some(EV_KEY::KEY_TEEN), + 415 => Some(EV_KEY::KEY_TWEN), + 416 => Some(EV_KEY::KEY_VIDEOPHONE), + 417 => Some(EV_KEY::KEY_GAMES), + 418 => Some(EV_KEY::KEY_ZOOMIN), + 419 => Some(EV_KEY::KEY_ZOOMOUT), + 420 => Some(EV_KEY::KEY_ZOOMRESET), + 421 => Some(EV_KEY::KEY_WORDPROCESSOR), + 422 => Some(EV_KEY::KEY_EDITOR), + 423 => Some(EV_KEY::KEY_SPREADSHEET), + 424 => Some(EV_KEY::KEY_GRAPHICSEDITOR), + 425 => Some(EV_KEY::KEY_PRESENTATION), + 426 => Some(EV_KEY::KEY_DATABASE), + 427 => Some(EV_KEY::KEY_NEWS), + 428 => Some(EV_KEY::KEY_VOICEMAIL), + 429 => Some(EV_KEY::KEY_ADDRESSBOOK), + 430 => Some(EV_KEY::KEY_MESSENGER), + 431 => Some(EV_KEY::KEY_DISPLAYTOGGLE), + 432 => Some(EV_KEY::KEY_SPELLCHECK), + 433 => Some(EV_KEY::KEY_LOGOFF), + 434 => Some(EV_KEY::KEY_DOLLAR), + 435 => Some(EV_KEY::KEY_EURO), + 436 => Some(EV_KEY::KEY_FRAMEBACK), + 437 => Some(EV_KEY::KEY_FRAMEFORWARD), + 438 => Some(EV_KEY::KEY_CONTEXT_MENU), + 439 => Some(EV_KEY::KEY_MEDIA_REPEAT), + 440 => Some(EV_KEY::KEY_10CHANNELSUP), + 441 => Some(EV_KEY::KEY_10CHANNELSDOWN), + 442 => Some(EV_KEY::KEY_IMAGES), + 444 => Some(EV_KEY::KEY_NOTIFICATION_CENTER), + 445 => Some(EV_KEY::KEY_PICKUP_PHONE), + 446 => Some(EV_KEY::KEY_HANGUP_PHONE), + 448 => Some(EV_KEY::KEY_DEL_EOL), + 449 => Some(EV_KEY::KEY_DEL_EOS), + 450 => Some(EV_KEY::KEY_INS_LINE), + 451 => Some(EV_KEY::KEY_DEL_LINE), + 464 => Some(EV_KEY::KEY_FN), + 465 => Some(EV_KEY::KEY_FN_ESC), + 466 => Some(EV_KEY::KEY_FN_F1), + 467 => Some(EV_KEY::KEY_FN_F2), + 468 => Some(EV_KEY::KEY_FN_F3), + 469 => Some(EV_KEY::KEY_FN_F4), + 470 => Some(EV_KEY::KEY_FN_F5), + 471 => Some(EV_KEY::KEY_FN_F6), + 472 => Some(EV_KEY::KEY_FN_F7), + 473 => Some(EV_KEY::KEY_FN_F8), + 474 => Some(EV_KEY::KEY_FN_F9), + 475 => Some(EV_KEY::KEY_FN_F10), + 476 => Some(EV_KEY::KEY_FN_F11), + 477 => Some(EV_KEY::KEY_FN_F12), + 478 => Some(EV_KEY::KEY_FN_1), + 479 => Some(EV_KEY::KEY_FN_2), + 480 => Some(EV_KEY::KEY_FN_D), + 481 => Some(EV_KEY::KEY_FN_E), + 482 => Some(EV_KEY::KEY_FN_F), + 483 => Some(EV_KEY::KEY_FN_S), + 484 => Some(EV_KEY::KEY_FN_B), + 485 => Some(EV_KEY::KEY_FN_RIGHT_SHIFT), + 497 => Some(EV_KEY::KEY_BRL_DOT1), + 498 => Some(EV_KEY::KEY_BRL_DOT2), + 499 => Some(EV_KEY::KEY_BRL_DOT3), + 500 => Some(EV_KEY::KEY_BRL_DOT4), + 501 => Some(EV_KEY::KEY_BRL_DOT5), + 502 => Some(EV_KEY::KEY_BRL_DOT6), + 503 => Some(EV_KEY::KEY_BRL_DOT7), + 504 => Some(EV_KEY::KEY_BRL_DOT8), + 505 => Some(EV_KEY::KEY_BRL_DOT9), + 506 => Some(EV_KEY::KEY_BRL_DOT10), + 512 => Some(EV_KEY::KEY_NUMERIC_0), + 513 => Some(EV_KEY::KEY_NUMERIC_1), + 514 => Some(EV_KEY::KEY_NUMERIC_2), + 515 => Some(EV_KEY::KEY_NUMERIC_3), + 516 => Some(EV_KEY::KEY_NUMERIC_4), + 517 => Some(EV_KEY::KEY_NUMERIC_5), + 518 => Some(EV_KEY::KEY_NUMERIC_6), + 519 => Some(EV_KEY::KEY_NUMERIC_7), + 520 => Some(EV_KEY::KEY_NUMERIC_8), + 521 => Some(EV_KEY::KEY_NUMERIC_9), + 522 => Some(EV_KEY::KEY_NUMERIC_STAR), + 523 => Some(EV_KEY::KEY_NUMERIC_POUND), + 524 => Some(EV_KEY::KEY_NUMERIC_A), + 525 => Some(EV_KEY::KEY_NUMERIC_B), + 526 => Some(EV_KEY::KEY_NUMERIC_C), + 527 => Some(EV_KEY::KEY_NUMERIC_D), + 528 => Some(EV_KEY::KEY_CAMERA_FOCUS), + 529 => Some(EV_KEY::KEY_WPS_BUTTON), + 530 => Some(EV_KEY::KEY_TOUCHPAD_TOGGLE), + 531 => Some(EV_KEY::KEY_TOUCHPAD_ON), + 532 => Some(EV_KEY::KEY_TOUCHPAD_OFF), + 533 => Some(EV_KEY::KEY_CAMERA_ZOOMIN), + 534 => Some(EV_KEY::KEY_CAMERA_ZOOMOUT), + 535 => Some(EV_KEY::KEY_CAMERA_UP), + 536 => Some(EV_KEY::KEY_CAMERA_DOWN), + 537 => Some(EV_KEY::KEY_CAMERA_LEFT), + 538 => Some(EV_KEY::KEY_CAMERA_RIGHT), + 539 => Some(EV_KEY::KEY_ATTENDANT_ON), + 540 => Some(EV_KEY::KEY_ATTENDANT_OFF), + 541 => Some(EV_KEY::KEY_ATTENDANT_TOGGLE), + 542 => Some(EV_KEY::KEY_LIGHTS_TOGGLE), + 560 => Some(EV_KEY::KEY_ALS_TOGGLE), + 561 => Some(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), + 562 => Some(EV_KEY::KEY_REFRESH_RATE_TOGGLE), + 576 => Some(EV_KEY::KEY_BUTTONCONFIG), + 577 => Some(EV_KEY::KEY_TASKMANAGER), + 578 => Some(EV_KEY::KEY_JOURNAL), + 579 => Some(EV_KEY::KEY_CONTROLPANEL), + 580 => Some(EV_KEY::KEY_APPSELECT), + 581 => Some(EV_KEY::KEY_SCREENSAVER), + 582 => Some(EV_KEY::KEY_VOICECOMMAND), + 583 => Some(EV_KEY::KEY_ASSISTANT), + 584 => Some(EV_KEY::KEY_KBD_LAYOUT_NEXT), + 585 => Some(EV_KEY::KEY_EMOJI_PICKER), + 586 => Some(EV_KEY::KEY_DICTATE), + 587 => Some(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), + 588 => Some(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), + 589 => Some(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), + 590 => Some(EV_KEY::KEY_ACCESSIBILITY), + 591 => Some(EV_KEY::KEY_DO_NOT_DISTURB), + 592 => Some(EV_KEY::KEY_BRIGHTNESS_MIN), + 593 => Some(EV_KEY::KEY_BRIGHTNESS_MAX), + 608 => Some(EV_KEY::KEY_KBDINPUTASSIST_PREV), + 609 => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXT), + 610 => Some(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), + 611 => Some(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), + 612 => Some(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), + 613 => Some(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), + 614 => Some(EV_KEY::KEY_RIGHT_UP), + 615 => Some(EV_KEY::KEY_RIGHT_DOWN), + 616 => Some(EV_KEY::KEY_LEFT_UP), + 617 => Some(EV_KEY::KEY_LEFT_DOWN), + 618 => Some(EV_KEY::KEY_ROOT_MENU), + 619 => Some(EV_KEY::KEY_MEDIA_TOP_MENU), + 620 => Some(EV_KEY::KEY_NUMERIC_11), + 621 => Some(EV_KEY::KEY_NUMERIC_12), + 622 => Some(EV_KEY::KEY_AUDIO_DESC), + 623 => Some(EV_KEY::KEY_3D_MODE), + 624 => Some(EV_KEY::KEY_NEXT_FAVORITE), + 625 => Some(EV_KEY::KEY_STOP_RECORD), + 626 => Some(EV_KEY::KEY_PAUSE_RECORD), + 627 => Some(EV_KEY::KEY_VOD), + 628 => Some(EV_KEY::KEY_UNMUTE), + 629 => Some(EV_KEY::KEY_FASTREVERSE), + 630 => Some(EV_KEY::KEY_SLOWREVERSE), + 631 => Some(EV_KEY::KEY_DATA), + 632 => Some(EV_KEY::KEY_ONSCREEN_KEYBOARD), + 633 => Some(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), + 634 => Some(EV_KEY::KEY_SELECTIVE_SCREENSHOT), + 635 => Some(EV_KEY::KEY_NEXT_ELEMENT), + 636 => Some(EV_KEY::KEY_PREVIOUS_ELEMENT), + 637 => Some(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), + 638 => Some(EV_KEY::KEY_MARK_WAYPOINT), + 639 => Some(EV_KEY::KEY_SOS), + 640 => Some(EV_KEY::KEY_NAV_CHART), + 641 => Some(EV_KEY::KEY_FISHING_CHART), + 642 => Some(EV_KEY::KEY_SINGLE_RANGE_RADAR), + 643 => Some(EV_KEY::KEY_DUAL_RANGE_RADAR), + 644 => Some(EV_KEY::KEY_RADAR_OVERLAY), + 645 => Some(EV_KEY::KEY_TRADITIONAL_SONAR), + 646 => Some(EV_KEY::KEY_CLEARVU_SONAR), + 647 => Some(EV_KEY::KEY_SIDEVU_SONAR), + 648 => Some(EV_KEY::KEY_NAV_INFO), + 649 => Some(EV_KEY::KEY_BRIGHTNESS_MENU), + 656 => Some(EV_KEY::KEY_MACRO1), + 657 => Some(EV_KEY::KEY_MACRO2), + 658 => Some(EV_KEY::KEY_MACRO3), + 659 => Some(EV_KEY::KEY_MACRO4), + 660 => Some(EV_KEY::KEY_MACRO5), + 661 => Some(EV_KEY::KEY_MACRO6), + 662 => Some(EV_KEY::KEY_MACRO7), + 663 => Some(EV_KEY::KEY_MACRO8), + 664 => Some(EV_KEY::KEY_MACRO9), + 665 => Some(EV_KEY::KEY_MACRO10), + 666 => Some(EV_KEY::KEY_MACRO11), + 667 => Some(EV_KEY::KEY_MACRO12), + 668 => Some(EV_KEY::KEY_MACRO13), + 669 => Some(EV_KEY::KEY_MACRO14), + 670 => Some(EV_KEY::KEY_MACRO15), + 671 => Some(EV_KEY::KEY_MACRO16), + 672 => Some(EV_KEY::KEY_MACRO17), + 673 => Some(EV_KEY::KEY_MACRO18), + 674 => Some(EV_KEY::KEY_MACRO19), + 675 => Some(EV_KEY::KEY_MACRO20), + 676 => Some(EV_KEY::KEY_MACRO21), + 677 => Some(EV_KEY::KEY_MACRO22), + 678 => Some(EV_KEY::KEY_MACRO23), + 679 => Some(EV_KEY::KEY_MACRO24), + 680 => Some(EV_KEY::KEY_MACRO25), + 681 => Some(EV_KEY::KEY_MACRO26), + 682 => Some(EV_KEY::KEY_MACRO27), + 683 => Some(EV_KEY::KEY_MACRO28), + 684 => Some(EV_KEY::KEY_MACRO29), + 685 => Some(EV_KEY::KEY_MACRO30), + 688 => Some(EV_KEY::KEY_MACRO_RECORD_START), + 689 => Some(EV_KEY::KEY_MACRO_RECORD_STOP), + 690 => Some(EV_KEY::KEY_MACRO_PRESET_CYCLE), + 691 => Some(EV_KEY::KEY_MACRO_PRESET1), + 692 => Some(EV_KEY::KEY_MACRO_PRESET2), + 693 => Some(EV_KEY::KEY_MACRO_PRESET3), + 696 => Some(EV_KEY::KEY_KBD_LCD_MENU1), + 697 => Some(EV_KEY::KEY_KBD_LCD_MENU2), + 698 => Some(EV_KEY::KEY_KBD_LCD_MENU3), + 699 => Some(EV_KEY::KEY_KBD_LCD_MENU4), + 700 => Some(EV_KEY::KEY_KBD_LCD_MENU5), + 767 => Some(EV_KEY::KEY_MAX), + 256 => Some(EV_KEY::BTN_0), + 257 => Some(EV_KEY::BTN_1), + 258 => Some(EV_KEY::BTN_2), + 259 => Some(EV_KEY::BTN_3), + 260 => Some(EV_KEY::BTN_4), + 261 => Some(EV_KEY::BTN_5), + 262 => Some(EV_KEY::BTN_6), + 263 => Some(EV_KEY::BTN_7), + 264 => Some(EV_KEY::BTN_8), + 265 => Some(EV_KEY::BTN_9), + 272 => Some(EV_KEY::BTN_LEFT), + 273 => Some(EV_KEY::BTN_RIGHT), + 274 => Some(EV_KEY::BTN_MIDDLE), + 275 => Some(EV_KEY::BTN_SIDE), + 276 => Some(EV_KEY::BTN_EXTRA), + 277 => Some(EV_KEY::BTN_FORWARD), + 278 => Some(EV_KEY::BTN_BACK), + 279 => Some(EV_KEY::BTN_TASK), + 288 => Some(EV_KEY::BTN_TRIGGER), + 289 => Some(EV_KEY::BTN_THUMB), + 290 => Some(EV_KEY::BTN_THUMB2), + 291 => Some(EV_KEY::BTN_TOP), + 292 => Some(EV_KEY::BTN_TOP2), + 293 => Some(EV_KEY::BTN_PINKIE), + 294 => Some(EV_KEY::BTN_BASE), + 295 => Some(EV_KEY::BTN_BASE2), + 296 => Some(EV_KEY::BTN_BASE3), + 297 => Some(EV_KEY::BTN_BASE4), + 298 => Some(EV_KEY::BTN_BASE5), + 299 => Some(EV_KEY::BTN_BASE6), + 303 => Some(EV_KEY::BTN_DEAD), + 304 => Some(EV_KEY::BTN_SOUTH), + 305 => Some(EV_KEY::BTN_EAST), + 306 => Some(EV_KEY::BTN_C), + 307 => Some(EV_KEY::BTN_NORTH), + 308 => Some(EV_KEY::BTN_WEST), + 309 => Some(EV_KEY::BTN_Z), + 310 => Some(EV_KEY::BTN_TL), + 311 => Some(EV_KEY::BTN_TR), + 312 => Some(EV_KEY::BTN_TL2), + 313 => Some(EV_KEY::BTN_TR2), + 314 => Some(EV_KEY::BTN_SELECT), + 315 => Some(EV_KEY::BTN_START), + 316 => Some(EV_KEY::BTN_MODE), + 317 => Some(EV_KEY::BTN_THUMBL), + 318 => Some(EV_KEY::BTN_THUMBR), + 320 => Some(EV_KEY::BTN_TOOL_PEN), + 321 => Some(EV_KEY::BTN_TOOL_RUBBER), + 322 => Some(EV_KEY::BTN_TOOL_BRUSH), + 323 => Some(EV_KEY::BTN_TOOL_PENCIL), + 324 => Some(EV_KEY::BTN_TOOL_AIRBRUSH), + 325 => Some(EV_KEY::BTN_TOOL_FINGER), + 326 => Some(EV_KEY::BTN_TOOL_MOUSE), + 327 => Some(EV_KEY::BTN_TOOL_LENS), + 328 => Some(EV_KEY::BTN_TOOL_QUINTTAP), + 329 => Some(EV_KEY::BTN_STYLUS3), + 330 => Some(EV_KEY::BTN_TOUCH), + 331 => Some(EV_KEY::BTN_STYLUS), + 332 => Some(EV_KEY::BTN_STYLUS2), + 333 => Some(EV_KEY::BTN_TOOL_DOUBLETAP), + 334 => Some(EV_KEY::BTN_TOOL_TRIPLETAP), + 335 => Some(EV_KEY::BTN_TOOL_QUADTAP), + 336 => Some(EV_KEY::BTN_GEAR_DOWN), + 337 => Some(EV_KEY::BTN_GEAR_UP), + 544 => Some(EV_KEY::BTN_DPAD_UP), + 545 => Some(EV_KEY::BTN_DPAD_DOWN), + 546 => Some(EV_KEY::BTN_DPAD_LEFT), + 547 => Some(EV_KEY::BTN_DPAD_RIGHT), + 704 => Some(EV_KEY::BTN_TRIGGER_HAPPY1), + 705 => Some(EV_KEY::BTN_TRIGGER_HAPPY2), + 706 => Some(EV_KEY::BTN_TRIGGER_HAPPY3), + 707 => Some(EV_KEY::BTN_TRIGGER_HAPPY4), + 708 => Some(EV_KEY::BTN_TRIGGER_HAPPY5), + 709 => Some(EV_KEY::BTN_TRIGGER_HAPPY6), + 710 => Some(EV_KEY::BTN_TRIGGER_HAPPY7), + 711 => Some(EV_KEY::BTN_TRIGGER_HAPPY8), + 712 => Some(EV_KEY::BTN_TRIGGER_HAPPY9), + 713 => Some(EV_KEY::BTN_TRIGGER_HAPPY10), + 714 => Some(EV_KEY::BTN_TRIGGER_HAPPY11), + 715 => Some(EV_KEY::BTN_TRIGGER_HAPPY12), + 716 => Some(EV_KEY::BTN_TRIGGER_HAPPY13), + 717 => Some(EV_KEY::BTN_TRIGGER_HAPPY14), + 718 => Some(EV_KEY::BTN_TRIGGER_HAPPY15), + 719 => Some(EV_KEY::BTN_TRIGGER_HAPPY16), + 720 => Some(EV_KEY::BTN_TRIGGER_HAPPY17), + 721 => Some(EV_KEY::BTN_TRIGGER_HAPPY18), + 722 => Some(EV_KEY::BTN_TRIGGER_HAPPY19), + 723 => Some(EV_KEY::BTN_TRIGGER_HAPPY20), + 724 => Some(EV_KEY::BTN_TRIGGER_HAPPY21), + 725 => Some(EV_KEY::BTN_TRIGGER_HAPPY22), + 726 => Some(EV_KEY::BTN_TRIGGER_HAPPY23), + 727 => Some(EV_KEY::BTN_TRIGGER_HAPPY24), + 728 => Some(EV_KEY::BTN_TRIGGER_HAPPY25), + 729 => Some(EV_KEY::BTN_TRIGGER_HAPPY26), + 730 => Some(EV_KEY::BTN_TRIGGER_HAPPY27), + 731 => Some(EV_KEY::BTN_TRIGGER_HAPPY28), + 732 => Some(EV_KEY::BTN_TRIGGER_HAPPY29), + 733 => Some(EV_KEY::BTN_TRIGGER_HAPPY30), + 734 => Some(EV_KEY::BTN_TRIGGER_HAPPY31), + 735 => Some(EV_KEY::BTN_TRIGGER_HAPPY32), + 736 => Some(EV_KEY::BTN_TRIGGER_HAPPY33), + 737 => Some(EV_KEY::BTN_TRIGGER_HAPPY34), + 738 => Some(EV_KEY::BTN_TRIGGER_HAPPY35), + 739 => Some(EV_KEY::BTN_TRIGGER_HAPPY36), + 740 => Some(EV_KEY::BTN_TRIGGER_HAPPY37), + 741 => Some(EV_KEY::BTN_TRIGGER_HAPPY38), + 742 => Some(EV_KEY::BTN_TRIGGER_HAPPY39), + 743 => Some(EV_KEY::BTN_TRIGGER_HAPPY40), + _ => None, + } +} + +impl std::str::FromStr for EV_KEY { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "KEY_RESERVED" => Ok(EV_KEY::KEY_RESERVED), + "KEY_ESC" => Ok(EV_KEY::KEY_ESC), + "KEY_1" => Ok(EV_KEY::KEY_1), + "KEY_2" => Ok(EV_KEY::KEY_2), + "KEY_3" => Ok(EV_KEY::KEY_3), + "KEY_4" => Ok(EV_KEY::KEY_4), + "KEY_5" => Ok(EV_KEY::KEY_5), + "KEY_6" => Ok(EV_KEY::KEY_6), + "KEY_7" => Ok(EV_KEY::KEY_7), + "KEY_8" => Ok(EV_KEY::KEY_8), + "KEY_9" => Ok(EV_KEY::KEY_9), + "KEY_0" => Ok(EV_KEY::KEY_0), + "KEY_MINUS" => Ok(EV_KEY::KEY_MINUS), + "KEY_EQUAL" => Ok(EV_KEY::KEY_EQUAL), + "KEY_BACKSPACE" => Ok(EV_KEY::KEY_BACKSPACE), + "KEY_TAB" => Ok(EV_KEY::KEY_TAB), + "KEY_Q" => Ok(EV_KEY::KEY_Q), + "KEY_W" => Ok(EV_KEY::KEY_W), + "KEY_E" => Ok(EV_KEY::KEY_E), + "KEY_R" => Ok(EV_KEY::KEY_R), + "KEY_T" => Ok(EV_KEY::KEY_T), + "KEY_Y" => Ok(EV_KEY::KEY_Y), + "KEY_U" => Ok(EV_KEY::KEY_U), + "KEY_I" => Ok(EV_KEY::KEY_I), + "KEY_O" => Ok(EV_KEY::KEY_O), + "KEY_P" => Ok(EV_KEY::KEY_P), + "KEY_LEFTBRACE" => Ok(EV_KEY::KEY_LEFTBRACE), + "KEY_RIGHTBRACE" => Ok(EV_KEY::KEY_RIGHTBRACE), + "KEY_ENTER" => Ok(EV_KEY::KEY_ENTER), + "KEY_LEFTCTRL" => Ok(EV_KEY::KEY_LEFTCTRL), + "KEY_A" => Ok(EV_KEY::KEY_A), + "KEY_S" => Ok(EV_KEY::KEY_S), + "KEY_D" => Ok(EV_KEY::KEY_D), + "KEY_F" => Ok(EV_KEY::KEY_F), + "KEY_G" => Ok(EV_KEY::KEY_G), + "KEY_H" => Ok(EV_KEY::KEY_H), + "KEY_J" => Ok(EV_KEY::KEY_J), + "KEY_K" => Ok(EV_KEY::KEY_K), + "KEY_L" => Ok(EV_KEY::KEY_L), + "KEY_SEMICOLON" => Ok(EV_KEY::KEY_SEMICOLON), + "KEY_APOSTROPHE" => Ok(EV_KEY::KEY_APOSTROPHE), + "KEY_GRAVE" => Ok(EV_KEY::KEY_GRAVE), + "KEY_LEFTSHIFT" => Ok(EV_KEY::KEY_LEFTSHIFT), + "KEY_BACKSLASH" => Ok(EV_KEY::KEY_BACKSLASH), + "KEY_Z" => Ok(EV_KEY::KEY_Z), + "KEY_X" => Ok(EV_KEY::KEY_X), + "KEY_C" => Ok(EV_KEY::KEY_C), + "KEY_V" => Ok(EV_KEY::KEY_V), + "KEY_B" => Ok(EV_KEY::KEY_B), + "KEY_N" => Ok(EV_KEY::KEY_N), + "KEY_M" => Ok(EV_KEY::KEY_M), + "KEY_COMMA" => Ok(EV_KEY::KEY_COMMA), + "KEY_DOT" => Ok(EV_KEY::KEY_DOT), + "KEY_SLASH" => Ok(EV_KEY::KEY_SLASH), + "KEY_RIGHTSHIFT" => Ok(EV_KEY::KEY_RIGHTSHIFT), + "KEY_KPASTERISK" => Ok(EV_KEY::KEY_KPASTERISK), + "KEY_LEFTALT" => Ok(EV_KEY::KEY_LEFTALT), + "KEY_SPACE" => Ok(EV_KEY::KEY_SPACE), + "KEY_CAPSLOCK" => Ok(EV_KEY::KEY_CAPSLOCK), + "KEY_F1" => Ok(EV_KEY::KEY_F1), + "KEY_F2" => Ok(EV_KEY::KEY_F2), + "KEY_F3" => Ok(EV_KEY::KEY_F3), + "KEY_F4" => Ok(EV_KEY::KEY_F4), + "KEY_F5" => Ok(EV_KEY::KEY_F5), + "KEY_F6" => Ok(EV_KEY::KEY_F6), + "KEY_F7" => Ok(EV_KEY::KEY_F7), + "KEY_F8" => Ok(EV_KEY::KEY_F8), + "KEY_F9" => Ok(EV_KEY::KEY_F9), + "KEY_F10" => Ok(EV_KEY::KEY_F10), + "KEY_NUMLOCK" => Ok(EV_KEY::KEY_NUMLOCK), + "KEY_SCROLLLOCK" => Ok(EV_KEY::KEY_SCROLLLOCK), + "KEY_KP7" => Ok(EV_KEY::KEY_KP7), + "KEY_KP8" => Ok(EV_KEY::KEY_KP8), + "KEY_KP9" => Ok(EV_KEY::KEY_KP9), + "KEY_KPMINUS" => Ok(EV_KEY::KEY_KPMINUS), + "KEY_KP4" => Ok(EV_KEY::KEY_KP4), + "KEY_KP5" => Ok(EV_KEY::KEY_KP5), + "KEY_KP6" => Ok(EV_KEY::KEY_KP6), + "KEY_KPPLUS" => Ok(EV_KEY::KEY_KPPLUS), + "KEY_KP1" => Ok(EV_KEY::KEY_KP1), + "KEY_KP2" => Ok(EV_KEY::KEY_KP2), + "KEY_KP3" => Ok(EV_KEY::KEY_KP3), + "KEY_KP0" => Ok(EV_KEY::KEY_KP0), + "KEY_KPDOT" => Ok(EV_KEY::KEY_KPDOT), + "KEY_ZENKAKUHANKAKU" => Ok(EV_KEY::KEY_ZENKAKUHANKAKU), + "KEY_102ND" => Ok(EV_KEY::KEY_102ND), + "KEY_F11" => Ok(EV_KEY::KEY_F11), + "KEY_F12" => Ok(EV_KEY::KEY_F12), + "KEY_RO" => Ok(EV_KEY::KEY_RO), + "KEY_KATAKANA" => Ok(EV_KEY::KEY_KATAKANA), + "KEY_HIRAGANA" => Ok(EV_KEY::KEY_HIRAGANA), + "KEY_HENKAN" => Ok(EV_KEY::KEY_HENKAN), + "KEY_KATAKANAHIRAGANA" => Ok(EV_KEY::KEY_KATAKANAHIRAGANA), + "KEY_MUHENKAN" => Ok(EV_KEY::KEY_MUHENKAN), + "KEY_KPJPCOMMA" => Ok(EV_KEY::KEY_KPJPCOMMA), + "KEY_KPENTER" => Ok(EV_KEY::KEY_KPENTER), + "KEY_RIGHTCTRL" => Ok(EV_KEY::KEY_RIGHTCTRL), + "KEY_KPSLASH" => Ok(EV_KEY::KEY_KPSLASH), + "KEY_SYSRQ" => Ok(EV_KEY::KEY_SYSRQ), + "KEY_RIGHTALT" => Ok(EV_KEY::KEY_RIGHTALT), + "KEY_LINEFEED" => Ok(EV_KEY::KEY_LINEFEED), + "KEY_HOME" => Ok(EV_KEY::KEY_HOME), + "KEY_UP" => Ok(EV_KEY::KEY_UP), + "KEY_PAGEUP" => Ok(EV_KEY::KEY_PAGEUP), + "KEY_LEFT" => Ok(EV_KEY::KEY_LEFT), + "KEY_RIGHT" => Ok(EV_KEY::KEY_RIGHT), + "KEY_END" => Ok(EV_KEY::KEY_END), + "KEY_DOWN" => Ok(EV_KEY::KEY_DOWN), + "KEY_PAGEDOWN" => Ok(EV_KEY::KEY_PAGEDOWN), + "KEY_INSERT" => Ok(EV_KEY::KEY_INSERT), + "KEY_DELETE" => Ok(EV_KEY::KEY_DELETE), + "KEY_MACRO" => Ok(EV_KEY::KEY_MACRO), + "KEY_MUTE" => Ok(EV_KEY::KEY_MUTE), + "KEY_VOLUMEDOWN" => Ok(EV_KEY::KEY_VOLUMEDOWN), + "KEY_VOLUMEUP" => Ok(EV_KEY::KEY_VOLUMEUP), + "KEY_POWER" => Ok(EV_KEY::KEY_POWER), + "KEY_KPEQUAL" => Ok(EV_KEY::KEY_KPEQUAL), + "KEY_KPPLUSMINUS" => Ok(EV_KEY::KEY_KPPLUSMINUS), + "KEY_PAUSE" => Ok(EV_KEY::KEY_PAUSE), + "KEY_SCALE" => Ok(EV_KEY::KEY_SCALE), + "KEY_KPCOMMA" => Ok(EV_KEY::KEY_KPCOMMA), + "KEY_HANGEUL" => Ok(EV_KEY::KEY_HANGEUL), + "KEY_HANJA" => Ok(EV_KEY::KEY_HANJA), + "KEY_YEN" => Ok(EV_KEY::KEY_YEN), + "KEY_LEFTMETA" => Ok(EV_KEY::KEY_LEFTMETA), + "KEY_RIGHTMETA" => Ok(EV_KEY::KEY_RIGHTMETA), + "KEY_COMPOSE" => Ok(EV_KEY::KEY_COMPOSE), + "KEY_STOP" => Ok(EV_KEY::KEY_STOP), + "KEY_AGAIN" => Ok(EV_KEY::KEY_AGAIN), + "KEY_PROPS" => Ok(EV_KEY::KEY_PROPS), + "KEY_UNDO" => Ok(EV_KEY::KEY_UNDO), + "KEY_FRONT" => Ok(EV_KEY::KEY_FRONT), + "KEY_COPY" => Ok(EV_KEY::KEY_COPY), + "KEY_OPEN" => Ok(EV_KEY::KEY_OPEN), + "KEY_PASTE" => Ok(EV_KEY::KEY_PASTE), + "KEY_FIND" => Ok(EV_KEY::KEY_FIND), + "KEY_CUT" => Ok(EV_KEY::KEY_CUT), + "KEY_HELP" => Ok(EV_KEY::KEY_HELP), + "KEY_MENU" => Ok(EV_KEY::KEY_MENU), + "KEY_CALC" => Ok(EV_KEY::KEY_CALC), + "KEY_SETUP" => Ok(EV_KEY::KEY_SETUP), + "KEY_SLEEP" => Ok(EV_KEY::KEY_SLEEP), + "KEY_WAKEUP" => Ok(EV_KEY::KEY_WAKEUP), + "KEY_FILE" => Ok(EV_KEY::KEY_FILE), + "KEY_SENDFILE" => Ok(EV_KEY::KEY_SENDFILE), + "KEY_DELETEFILE" => Ok(EV_KEY::KEY_DELETEFILE), + "KEY_XFER" => Ok(EV_KEY::KEY_XFER), + "KEY_PROG1" => Ok(EV_KEY::KEY_PROG1), + "KEY_PROG2" => Ok(EV_KEY::KEY_PROG2), + "KEY_WWW" => Ok(EV_KEY::KEY_WWW), + "KEY_MSDOS" => Ok(EV_KEY::KEY_MSDOS), + "KEY_COFFEE" => Ok(EV_KEY::KEY_COFFEE), + "KEY_ROTATE_DISPLAY" => Ok(EV_KEY::KEY_ROTATE_DISPLAY), + "KEY_CYCLEWINDOWS" => Ok(EV_KEY::KEY_CYCLEWINDOWS), + "KEY_MAIL" => Ok(EV_KEY::KEY_MAIL), + "KEY_BOOKMARKS" => Ok(EV_KEY::KEY_BOOKMARKS), + "KEY_COMPUTER" => Ok(EV_KEY::KEY_COMPUTER), + "KEY_BACK" => Ok(EV_KEY::KEY_BACK), + "KEY_FORWARD" => Ok(EV_KEY::KEY_FORWARD), + "KEY_CLOSECD" => Ok(EV_KEY::KEY_CLOSECD), + "KEY_EJECTCD" => Ok(EV_KEY::KEY_EJECTCD), + "KEY_EJECTCLOSECD" => Ok(EV_KEY::KEY_EJECTCLOSECD), + "KEY_NEXTSONG" => Ok(EV_KEY::KEY_NEXTSONG), + "KEY_PLAYPAUSE" => Ok(EV_KEY::KEY_PLAYPAUSE), + "KEY_PREVIOUSSONG" => Ok(EV_KEY::KEY_PREVIOUSSONG), + "KEY_STOPCD" => Ok(EV_KEY::KEY_STOPCD), + "KEY_RECORD" => Ok(EV_KEY::KEY_RECORD), + "KEY_REWIND" => Ok(EV_KEY::KEY_REWIND), + "KEY_PHONE" => Ok(EV_KEY::KEY_PHONE), + "KEY_ISO" => Ok(EV_KEY::KEY_ISO), + "KEY_CONFIG" => Ok(EV_KEY::KEY_CONFIG), + "KEY_HOMEPAGE" => Ok(EV_KEY::KEY_HOMEPAGE), + "KEY_REFRESH" => Ok(EV_KEY::KEY_REFRESH), + "KEY_EXIT" => Ok(EV_KEY::KEY_EXIT), + "KEY_MOVE" => Ok(EV_KEY::KEY_MOVE), + "KEY_EDIT" => Ok(EV_KEY::KEY_EDIT), + "KEY_SCROLLUP" => Ok(EV_KEY::KEY_SCROLLUP), + "KEY_SCROLLDOWN" => Ok(EV_KEY::KEY_SCROLLDOWN), + "KEY_KPLEFTPAREN" => Ok(EV_KEY::KEY_KPLEFTPAREN), + "KEY_KPRIGHTPAREN" => Ok(EV_KEY::KEY_KPRIGHTPAREN), + "KEY_NEW" => Ok(EV_KEY::KEY_NEW), + "KEY_REDO" => Ok(EV_KEY::KEY_REDO), + "KEY_F13" => Ok(EV_KEY::KEY_F13), + "KEY_F14" => Ok(EV_KEY::KEY_F14), + "KEY_F15" => Ok(EV_KEY::KEY_F15), + "KEY_F16" => Ok(EV_KEY::KEY_F16), + "KEY_F17" => Ok(EV_KEY::KEY_F17), + "KEY_F18" => Ok(EV_KEY::KEY_F18), + "KEY_F19" => Ok(EV_KEY::KEY_F19), + "KEY_F20" => Ok(EV_KEY::KEY_F20), + "KEY_F21" => Ok(EV_KEY::KEY_F21), + "KEY_F22" => Ok(EV_KEY::KEY_F22), + "KEY_F23" => Ok(EV_KEY::KEY_F23), + "KEY_F24" => Ok(EV_KEY::KEY_F24), + "KEY_PLAYCD" => Ok(EV_KEY::KEY_PLAYCD), + "KEY_PAUSECD" => Ok(EV_KEY::KEY_PAUSECD), + "KEY_PROG3" => Ok(EV_KEY::KEY_PROG3), + "KEY_PROG4" => Ok(EV_KEY::KEY_PROG4), + "KEY_ALL_APPLICATIONS" => Ok(EV_KEY::KEY_ALL_APPLICATIONS), + "KEY_SUSPEND" => Ok(EV_KEY::KEY_SUSPEND), + "KEY_CLOSE" => Ok(EV_KEY::KEY_CLOSE), + "KEY_PLAY" => Ok(EV_KEY::KEY_PLAY), + "KEY_FASTFORWARD" => Ok(EV_KEY::KEY_FASTFORWARD), + "KEY_BASSBOOST" => Ok(EV_KEY::KEY_BASSBOOST), + "KEY_PRINT" => Ok(EV_KEY::KEY_PRINT), + "KEY_HP" => Ok(EV_KEY::KEY_HP), + "KEY_CAMERA" => Ok(EV_KEY::KEY_CAMERA), + "KEY_SOUND" => Ok(EV_KEY::KEY_SOUND), + "KEY_QUESTION" => Ok(EV_KEY::KEY_QUESTION), + "KEY_EMAIL" => Ok(EV_KEY::KEY_EMAIL), + "KEY_CHAT" => Ok(EV_KEY::KEY_CHAT), + "KEY_SEARCH" => Ok(EV_KEY::KEY_SEARCH), + "KEY_CONNECT" => Ok(EV_KEY::KEY_CONNECT), + "KEY_FINANCE" => Ok(EV_KEY::KEY_FINANCE), + "KEY_SPORT" => Ok(EV_KEY::KEY_SPORT), + "KEY_SHOP" => Ok(EV_KEY::KEY_SHOP), + "KEY_ALTERASE" => Ok(EV_KEY::KEY_ALTERASE), + "KEY_CANCEL" => Ok(EV_KEY::KEY_CANCEL), + "KEY_BRIGHTNESSDOWN" => Ok(EV_KEY::KEY_BRIGHTNESSDOWN), + "KEY_BRIGHTNESSUP" => Ok(EV_KEY::KEY_BRIGHTNESSUP), + "KEY_MEDIA" => Ok(EV_KEY::KEY_MEDIA), + "KEY_SWITCHVIDEOMODE" => Ok(EV_KEY::KEY_SWITCHVIDEOMODE), + "KEY_KBDILLUMTOGGLE" => Ok(EV_KEY::KEY_KBDILLUMTOGGLE), + "KEY_KBDILLUMDOWN" => Ok(EV_KEY::KEY_KBDILLUMDOWN), + "KEY_KBDILLUMUP" => Ok(EV_KEY::KEY_KBDILLUMUP), + "KEY_SEND" => Ok(EV_KEY::KEY_SEND), + "KEY_REPLY" => Ok(EV_KEY::KEY_REPLY), + "KEY_FORWARDMAIL" => Ok(EV_KEY::KEY_FORWARDMAIL), + "KEY_SAVE" => Ok(EV_KEY::KEY_SAVE), + "KEY_DOCUMENTS" => Ok(EV_KEY::KEY_DOCUMENTS), + "KEY_BATTERY" => Ok(EV_KEY::KEY_BATTERY), + "KEY_BLUETOOTH" => Ok(EV_KEY::KEY_BLUETOOTH), + "KEY_WLAN" => Ok(EV_KEY::KEY_WLAN), + "KEY_UWB" => Ok(EV_KEY::KEY_UWB), + "KEY_UNKNOWN" => Ok(EV_KEY::KEY_UNKNOWN), + "KEY_VIDEO_NEXT" => Ok(EV_KEY::KEY_VIDEO_NEXT), + "KEY_VIDEO_PREV" => Ok(EV_KEY::KEY_VIDEO_PREV), + "KEY_BRIGHTNESS_CYCLE" => Ok(EV_KEY::KEY_BRIGHTNESS_CYCLE), + "KEY_BRIGHTNESS_AUTO" => Ok(EV_KEY::KEY_BRIGHTNESS_AUTO), + "KEY_DISPLAY_OFF" => Ok(EV_KEY::KEY_DISPLAY_OFF), + "KEY_WWAN" => Ok(EV_KEY::KEY_WWAN), + "KEY_RFKILL" => Ok(EV_KEY::KEY_RFKILL), + "KEY_MICMUTE" => Ok(EV_KEY::KEY_MICMUTE), + "KEY_OK" => Ok(EV_KEY::KEY_OK), + "KEY_SELECT" => Ok(EV_KEY::KEY_SELECT), + "KEY_GOTO" => Ok(EV_KEY::KEY_GOTO), + "KEY_CLEAR" => Ok(EV_KEY::KEY_CLEAR), + "KEY_POWER2" => Ok(EV_KEY::KEY_POWER2), + "KEY_OPTION" => Ok(EV_KEY::KEY_OPTION), + "KEY_INFO" => Ok(EV_KEY::KEY_INFO), + "KEY_TIME" => Ok(EV_KEY::KEY_TIME), + "KEY_VENDOR" => Ok(EV_KEY::KEY_VENDOR), + "KEY_ARCHIVE" => Ok(EV_KEY::KEY_ARCHIVE), + "KEY_PROGRAM" => Ok(EV_KEY::KEY_PROGRAM), + "KEY_CHANNEL" => Ok(EV_KEY::KEY_CHANNEL), + "KEY_FAVORITES" => Ok(EV_KEY::KEY_FAVORITES), + "KEY_EPG" => Ok(EV_KEY::KEY_EPG), + "KEY_PVR" => Ok(EV_KEY::KEY_PVR), + "KEY_MHP" => Ok(EV_KEY::KEY_MHP), + "KEY_LANGUAGE" => Ok(EV_KEY::KEY_LANGUAGE), + "KEY_TITLE" => Ok(EV_KEY::KEY_TITLE), + "KEY_SUBTITLE" => Ok(EV_KEY::KEY_SUBTITLE), + "KEY_ANGLE" => Ok(EV_KEY::KEY_ANGLE), + "KEY_FULL_SCREEN" => Ok(EV_KEY::KEY_FULL_SCREEN), + "KEY_MODE" => Ok(EV_KEY::KEY_MODE), + "KEY_KEYBOARD" => Ok(EV_KEY::KEY_KEYBOARD), + "KEY_ASPECT_RATIO" => Ok(EV_KEY::KEY_ASPECT_RATIO), + "KEY_PC" => Ok(EV_KEY::KEY_PC), + "KEY_TV" => Ok(EV_KEY::KEY_TV), + "KEY_TV2" => Ok(EV_KEY::KEY_TV2), + "KEY_VCR" => Ok(EV_KEY::KEY_VCR), + "KEY_VCR2" => Ok(EV_KEY::KEY_VCR2), + "KEY_SAT" => Ok(EV_KEY::KEY_SAT), + "KEY_SAT2" => Ok(EV_KEY::KEY_SAT2), + "KEY_CD" => Ok(EV_KEY::KEY_CD), + "KEY_TAPE" => Ok(EV_KEY::KEY_TAPE), + "KEY_RADIO" => Ok(EV_KEY::KEY_RADIO), + "KEY_TUNER" => Ok(EV_KEY::KEY_TUNER), + "KEY_PLAYER" => Ok(EV_KEY::KEY_PLAYER), + "KEY_TEXT" => Ok(EV_KEY::KEY_TEXT), + "KEY_DVD" => Ok(EV_KEY::KEY_DVD), + "KEY_AUX" => Ok(EV_KEY::KEY_AUX), + "KEY_MP3" => Ok(EV_KEY::KEY_MP3), + "KEY_AUDIO" => Ok(EV_KEY::KEY_AUDIO), + "KEY_VIDEO" => Ok(EV_KEY::KEY_VIDEO), + "KEY_DIRECTORY" => Ok(EV_KEY::KEY_DIRECTORY), + "KEY_LIST" => Ok(EV_KEY::KEY_LIST), + "KEY_MEMO" => Ok(EV_KEY::KEY_MEMO), + "KEY_CALENDAR" => Ok(EV_KEY::KEY_CALENDAR), + "KEY_RED" => Ok(EV_KEY::KEY_RED), + "KEY_GREEN" => Ok(EV_KEY::KEY_GREEN), + "KEY_YELLOW" => Ok(EV_KEY::KEY_YELLOW), + "KEY_BLUE" => Ok(EV_KEY::KEY_BLUE), + "KEY_CHANNELUP" => Ok(EV_KEY::KEY_CHANNELUP), + "KEY_CHANNELDOWN" => Ok(EV_KEY::KEY_CHANNELDOWN), + "KEY_FIRST" => Ok(EV_KEY::KEY_FIRST), + "KEY_LAST" => Ok(EV_KEY::KEY_LAST), + "KEY_AB" => Ok(EV_KEY::KEY_AB), + "KEY_NEXT" => Ok(EV_KEY::KEY_NEXT), + "KEY_RESTART" => Ok(EV_KEY::KEY_RESTART), + "KEY_SLOW" => Ok(EV_KEY::KEY_SLOW), + "KEY_SHUFFLE" => Ok(EV_KEY::KEY_SHUFFLE), + "KEY_BREAK" => Ok(EV_KEY::KEY_BREAK), + "KEY_PREVIOUS" => Ok(EV_KEY::KEY_PREVIOUS), + "KEY_DIGITS" => Ok(EV_KEY::KEY_DIGITS), + "KEY_TEEN" => Ok(EV_KEY::KEY_TEEN), + "KEY_TWEN" => Ok(EV_KEY::KEY_TWEN), + "KEY_VIDEOPHONE" => Ok(EV_KEY::KEY_VIDEOPHONE), + "KEY_GAMES" => Ok(EV_KEY::KEY_GAMES), + "KEY_ZOOMIN" => Ok(EV_KEY::KEY_ZOOMIN), + "KEY_ZOOMOUT" => Ok(EV_KEY::KEY_ZOOMOUT), + "KEY_ZOOMRESET" => Ok(EV_KEY::KEY_ZOOMRESET), + "KEY_WORDPROCESSOR" => Ok(EV_KEY::KEY_WORDPROCESSOR), + "KEY_EDITOR" => Ok(EV_KEY::KEY_EDITOR), + "KEY_SPREADSHEET" => Ok(EV_KEY::KEY_SPREADSHEET), + "KEY_GRAPHICSEDITOR" => Ok(EV_KEY::KEY_GRAPHICSEDITOR), + "KEY_PRESENTATION" => Ok(EV_KEY::KEY_PRESENTATION), + "KEY_DATABASE" => Ok(EV_KEY::KEY_DATABASE), + "KEY_NEWS" => Ok(EV_KEY::KEY_NEWS), + "KEY_VOICEMAIL" => Ok(EV_KEY::KEY_VOICEMAIL), + "KEY_ADDRESSBOOK" => Ok(EV_KEY::KEY_ADDRESSBOOK), + "KEY_MESSENGER" => Ok(EV_KEY::KEY_MESSENGER), + "KEY_DISPLAYTOGGLE" => Ok(EV_KEY::KEY_DISPLAYTOGGLE), + "KEY_SPELLCHECK" => Ok(EV_KEY::KEY_SPELLCHECK), + "KEY_LOGOFF" => Ok(EV_KEY::KEY_LOGOFF), + "KEY_DOLLAR" => Ok(EV_KEY::KEY_DOLLAR), + "KEY_EURO" => Ok(EV_KEY::KEY_EURO), + "KEY_FRAMEBACK" => Ok(EV_KEY::KEY_FRAMEBACK), + "KEY_FRAMEFORWARD" => Ok(EV_KEY::KEY_FRAMEFORWARD), + "KEY_CONTEXT_MENU" => Ok(EV_KEY::KEY_CONTEXT_MENU), + "KEY_MEDIA_REPEAT" => Ok(EV_KEY::KEY_MEDIA_REPEAT), + "KEY_10CHANNELSUP" => Ok(EV_KEY::KEY_10CHANNELSUP), + "KEY_10CHANNELSDOWN" => Ok(EV_KEY::KEY_10CHANNELSDOWN), + "KEY_IMAGES" => Ok(EV_KEY::KEY_IMAGES), + "KEY_NOTIFICATION_CENTER" => Ok(EV_KEY::KEY_NOTIFICATION_CENTER), + "KEY_PICKUP_PHONE" => Ok(EV_KEY::KEY_PICKUP_PHONE), + "KEY_HANGUP_PHONE" => Ok(EV_KEY::KEY_HANGUP_PHONE), + "KEY_DEL_EOL" => Ok(EV_KEY::KEY_DEL_EOL), + "KEY_DEL_EOS" => Ok(EV_KEY::KEY_DEL_EOS), + "KEY_INS_LINE" => Ok(EV_KEY::KEY_INS_LINE), + "KEY_DEL_LINE" => Ok(EV_KEY::KEY_DEL_LINE), + "KEY_FN" => Ok(EV_KEY::KEY_FN), + "KEY_FN_ESC" => Ok(EV_KEY::KEY_FN_ESC), + "KEY_FN_F1" => Ok(EV_KEY::KEY_FN_F1), + "KEY_FN_F2" => Ok(EV_KEY::KEY_FN_F2), + "KEY_FN_F3" => Ok(EV_KEY::KEY_FN_F3), + "KEY_FN_F4" => Ok(EV_KEY::KEY_FN_F4), + "KEY_FN_F5" => Ok(EV_KEY::KEY_FN_F5), + "KEY_FN_F6" => Ok(EV_KEY::KEY_FN_F6), + "KEY_FN_F7" => Ok(EV_KEY::KEY_FN_F7), + "KEY_FN_F8" => Ok(EV_KEY::KEY_FN_F8), + "KEY_FN_F9" => Ok(EV_KEY::KEY_FN_F9), + "KEY_FN_F10" => Ok(EV_KEY::KEY_FN_F10), + "KEY_FN_F11" => Ok(EV_KEY::KEY_FN_F11), + "KEY_FN_F12" => Ok(EV_KEY::KEY_FN_F12), + "KEY_FN_1" => Ok(EV_KEY::KEY_FN_1), + "KEY_FN_2" => Ok(EV_KEY::KEY_FN_2), + "KEY_FN_D" => Ok(EV_KEY::KEY_FN_D), + "KEY_FN_E" => Ok(EV_KEY::KEY_FN_E), + "KEY_FN_F" => Ok(EV_KEY::KEY_FN_F), + "KEY_FN_S" => Ok(EV_KEY::KEY_FN_S), + "KEY_FN_B" => Ok(EV_KEY::KEY_FN_B), + "KEY_FN_RIGHT_SHIFT" => Ok(EV_KEY::KEY_FN_RIGHT_SHIFT), + "KEY_BRL_DOT1" => Ok(EV_KEY::KEY_BRL_DOT1), + "KEY_BRL_DOT2" => Ok(EV_KEY::KEY_BRL_DOT2), + "KEY_BRL_DOT3" => Ok(EV_KEY::KEY_BRL_DOT3), + "KEY_BRL_DOT4" => Ok(EV_KEY::KEY_BRL_DOT4), + "KEY_BRL_DOT5" => Ok(EV_KEY::KEY_BRL_DOT5), + "KEY_BRL_DOT6" => Ok(EV_KEY::KEY_BRL_DOT6), + "KEY_BRL_DOT7" => Ok(EV_KEY::KEY_BRL_DOT7), + "KEY_BRL_DOT8" => Ok(EV_KEY::KEY_BRL_DOT8), + "KEY_BRL_DOT9" => Ok(EV_KEY::KEY_BRL_DOT9), + "KEY_BRL_DOT10" => Ok(EV_KEY::KEY_BRL_DOT10), + "KEY_NUMERIC_0" => Ok(EV_KEY::KEY_NUMERIC_0), + "KEY_NUMERIC_1" => Ok(EV_KEY::KEY_NUMERIC_1), + "KEY_NUMERIC_2" => Ok(EV_KEY::KEY_NUMERIC_2), + "KEY_NUMERIC_3" => Ok(EV_KEY::KEY_NUMERIC_3), + "KEY_NUMERIC_4" => Ok(EV_KEY::KEY_NUMERIC_4), + "KEY_NUMERIC_5" => Ok(EV_KEY::KEY_NUMERIC_5), + "KEY_NUMERIC_6" => Ok(EV_KEY::KEY_NUMERIC_6), + "KEY_NUMERIC_7" => Ok(EV_KEY::KEY_NUMERIC_7), + "KEY_NUMERIC_8" => Ok(EV_KEY::KEY_NUMERIC_8), + "KEY_NUMERIC_9" => Ok(EV_KEY::KEY_NUMERIC_9), + "KEY_NUMERIC_STAR" => Ok(EV_KEY::KEY_NUMERIC_STAR), + "KEY_NUMERIC_POUND" => Ok(EV_KEY::KEY_NUMERIC_POUND), + "KEY_NUMERIC_A" => Ok(EV_KEY::KEY_NUMERIC_A), + "KEY_NUMERIC_B" => Ok(EV_KEY::KEY_NUMERIC_B), + "KEY_NUMERIC_C" => Ok(EV_KEY::KEY_NUMERIC_C), + "KEY_NUMERIC_D" => Ok(EV_KEY::KEY_NUMERIC_D), + "KEY_CAMERA_FOCUS" => Ok(EV_KEY::KEY_CAMERA_FOCUS), + "KEY_WPS_BUTTON" => Ok(EV_KEY::KEY_WPS_BUTTON), + "KEY_TOUCHPAD_TOGGLE" => Ok(EV_KEY::KEY_TOUCHPAD_TOGGLE), + "KEY_TOUCHPAD_ON" => Ok(EV_KEY::KEY_TOUCHPAD_ON), + "KEY_TOUCHPAD_OFF" => Ok(EV_KEY::KEY_TOUCHPAD_OFF), + "KEY_CAMERA_ZOOMIN" => Ok(EV_KEY::KEY_CAMERA_ZOOMIN), + "KEY_CAMERA_ZOOMOUT" => Ok(EV_KEY::KEY_CAMERA_ZOOMOUT), + "KEY_CAMERA_UP" => Ok(EV_KEY::KEY_CAMERA_UP), + "KEY_CAMERA_DOWN" => Ok(EV_KEY::KEY_CAMERA_DOWN), + "KEY_CAMERA_LEFT" => Ok(EV_KEY::KEY_CAMERA_LEFT), + "KEY_CAMERA_RIGHT" => Ok(EV_KEY::KEY_CAMERA_RIGHT), + "KEY_ATTENDANT_ON" => Ok(EV_KEY::KEY_ATTENDANT_ON), + "KEY_ATTENDANT_OFF" => Ok(EV_KEY::KEY_ATTENDANT_OFF), + "KEY_ATTENDANT_TOGGLE" => Ok(EV_KEY::KEY_ATTENDANT_TOGGLE), + "KEY_LIGHTS_TOGGLE" => Ok(EV_KEY::KEY_LIGHTS_TOGGLE), + "KEY_ALS_TOGGLE" => Ok(EV_KEY::KEY_ALS_TOGGLE), + "KEY_ROTATE_LOCK_TOGGLE" => Ok(EV_KEY::KEY_ROTATE_LOCK_TOGGLE), + "KEY_REFRESH_RATE_TOGGLE" => Ok(EV_KEY::KEY_REFRESH_RATE_TOGGLE), + "KEY_BUTTONCONFIG" => Ok(EV_KEY::KEY_BUTTONCONFIG), + "KEY_TASKMANAGER" => Ok(EV_KEY::KEY_TASKMANAGER), + "KEY_JOURNAL" => Ok(EV_KEY::KEY_JOURNAL), + "KEY_CONTROLPANEL" => Ok(EV_KEY::KEY_CONTROLPANEL), + "KEY_APPSELECT" => Ok(EV_KEY::KEY_APPSELECT), + "KEY_SCREENSAVER" => Ok(EV_KEY::KEY_SCREENSAVER), + "KEY_VOICECOMMAND" => Ok(EV_KEY::KEY_VOICECOMMAND), + "KEY_ASSISTANT" => Ok(EV_KEY::KEY_ASSISTANT), + "KEY_KBD_LAYOUT_NEXT" => Ok(EV_KEY::KEY_KBD_LAYOUT_NEXT), + "KEY_EMOJI_PICKER" => Ok(EV_KEY::KEY_EMOJI_PICKER), + "KEY_DICTATE" => Ok(EV_KEY::KEY_DICTATE), + "KEY_CAMERA_ACCESS_ENABLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_ENABLE), + "KEY_CAMERA_ACCESS_DISABLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_DISABLE), + "KEY_CAMERA_ACCESS_TOGGLE" => Ok(EV_KEY::KEY_CAMERA_ACCESS_TOGGLE), + "KEY_ACCESSIBILITY" => Ok(EV_KEY::KEY_ACCESSIBILITY), + "KEY_DO_NOT_DISTURB" => Ok(EV_KEY::KEY_DO_NOT_DISTURB), + "KEY_BRIGHTNESS_MIN" => Ok(EV_KEY::KEY_BRIGHTNESS_MIN), + "KEY_BRIGHTNESS_MAX" => Ok(EV_KEY::KEY_BRIGHTNESS_MAX), + "KEY_KBDINPUTASSIST_PREV" => Ok(EV_KEY::KEY_KBDINPUTASSIST_PREV), + "KEY_KBDINPUTASSIST_NEXT" => Ok(EV_KEY::KEY_KBDINPUTASSIST_NEXT), + "KEY_KBDINPUTASSIST_PREVGROUP" => Ok(EV_KEY::KEY_KBDINPUTASSIST_PREVGROUP), + "KEY_KBDINPUTASSIST_NEXTGROUP" => Ok(EV_KEY::KEY_KBDINPUTASSIST_NEXTGROUP), + "KEY_KBDINPUTASSIST_ACCEPT" => Ok(EV_KEY::KEY_KBDINPUTASSIST_ACCEPT), + "KEY_KBDINPUTASSIST_CANCEL" => Ok(EV_KEY::KEY_KBDINPUTASSIST_CANCEL), + "KEY_RIGHT_UP" => Ok(EV_KEY::KEY_RIGHT_UP), + "KEY_RIGHT_DOWN" => Ok(EV_KEY::KEY_RIGHT_DOWN), + "KEY_LEFT_UP" => Ok(EV_KEY::KEY_LEFT_UP), + "KEY_LEFT_DOWN" => Ok(EV_KEY::KEY_LEFT_DOWN), + "KEY_ROOT_MENU" => Ok(EV_KEY::KEY_ROOT_MENU), + "KEY_MEDIA_TOP_MENU" => Ok(EV_KEY::KEY_MEDIA_TOP_MENU), + "KEY_NUMERIC_11" => Ok(EV_KEY::KEY_NUMERIC_11), + "KEY_NUMERIC_12" => Ok(EV_KEY::KEY_NUMERIC_12), + "KEY_AUDIO_DESC" => Ok(EV_KEY::KEY_AUDIO_DESC), + "KEY_3D_MODE" => Ok(EV_KEY::KEY_3D_MODE), + "KEY_NEXT_FAVORITE" => Ok(EV_KEY::KEY_NEXT_FAVORITE), + "KEY_STOP_RECORD" => Ok(EV_KEY::KEY_STOP_RECORD), + "KEY_PAUSE_RECORD" => Ok(EV_KEY::KEY_PAUSE_RECORD), + "KEY_VOD" => Ok(EV_KEY::KEY_VOD), + "KEY_UNMUTE" => Ok(EV_KEY::KEY_UNMUTE), + "KEY_FASTREVERSE" => Ok(EV_KEY::KEY_FASTREVERSE), + "KEY_SLOWREVERSE" => Ok(EV_KEY::KEY_SLOWREVERSE), + "KEY_DATA" => Ok(EV_KEY::KEY_DATA), + "KEY_ONSCREEN_KEYBOARD" => Ok(EV_KEY::KEY_ONSCREEN_KEYBOARD), + "KEY_PRIVACY_SCREEN_TOGGLE" => Ok(EV_KEY::KEY_PRIVACY_SCREEN_TOGGLE), + "KEY_SELECTIVE_SCREENSHOT" => Ok(EV_KEY::KEY_SELECTIVE_SCREENSHOT), + "KEY_NEXT_ELEMENT" => Ok(EV_KEY::KEY_NEXT_ELEMENT), + "KEY_PREVIOUS_ELEMENT" => Ok(EV_KEY::KEY_PREVIOUS_ELEMENT), + "KEY_AUTOPILOT_ENGAGE_TOGGLE" => Ok(EV_KEY::KEY_AUTOPILOT_ENGAGE_TOGGLE), + "KEY_MARK_WAYPOINT" => Ok(EV_KEY::KEY_MARK_WAYPOINT), + "KEY_SOS" => Ok(EV_KEY::KEY_SOS), + "KEY_NAV_CHART" => Ok(EV_KEY::KEY_NAV_CHART), + "KEY_FISHING_CHART" => Ok(EV_KEY::KEY_FISHING_CHART), + "KEY_SINGLE_RANGE_RADAR" => Ok(EV_KEY::KEY_SINGLE_RANGE_RADAR), + "KEY_DUAL_RANGE_RADAR" => Ok(EV_KEY::KEY_DUAL_RANGE_RADAR), + "KEY_RADAR_OVERLAY" => Ok(EV_KEY::KEY_RADAR_OVERLAY), + "KEY_TRADITIONAL_SONAR" => Ok(EV_KEY::KEY_TRADITIONAL_SONAR), + "KEY_CLEARVU_SONAR" => Ok(EV_KEY::KEY_CLEARVU_SONAR), + "KEY_SIDEVU_SONAR" => Ok(EV_KEY::KEY_SIDEVU_SONAR), + "KEY_NAV_INFO" => Ok(EV_KEY::KEY_NAV_INFO), + "KEY_BRIGHTNESS_MENU" => Ok(EV_KEY::KEY_BRIGHTNESS_MENU), + "KEY_MACRO1" => Ok(EV_KEY::KEY_MACRO1), + "KEY_MACRO2" => Ok(EV_KEY::KEY_MACRO2), + "KEY_MACRO3" => Ok(EV_KEY::KEY_MACRO3), + "KEY_MACRO4" => Ok(EV_KEY::KEY_MACRO4), + "KEY_MACRO5" => Ok(EV_KEY::KEY_MACRO5), + "KEY_MACRO6" => Ok(EV_KEY::KEY_MACRO6), + "KEY_MACRO7" => Ok(EV_KEY::KEY_MACRO7), + "KEY_MACRO8" => Ok(EV_KEY::KEY_MACRO8), + "KEY_MACRO9" => Ok(EV_KEY::KEY_MACRO9), + "KEY_MACRO10" => Ok(EV_KEY::KEY_MACRO10), + "KEY_MACRO11" => Ok(EV_KEY::KEY_MACRO11), + "KEY_MACRO12" => Ok(EV_KEY::KEY_MACRO12), + "KEY_MACRO13" => Ok(EV_KEY::KEY_MACRO13), + "KEY_MACRO14" => Ok(EV_KEY::KEY_MACRO14), + "KEY_MACRO15" => Ok(EV_KEY::KEY_MACRO15), + "KEY_MACRO16" => Ok(EV_KEY::KEY_MACRO16), + "KEY_MACRO17" => Ok(EV_KEY::KEY_MACRO17), + "KEY_MACRO18" => Ok(EV_KEY::KEY_MACRO18), + "KEY_MACRO19" => Ok(EV_KEY::KEY_MACRO19), + "KEY_MACRO20" => Ok(EV_KEY::KEY_MACRO20), + "KEY_MACRO21" => Ok(EV_KEY::KEY_MACRO21), + "KEY_MACRO22" => Ok(EV_KEY::KEY_MACRO22), + "KEY_MACRO23" => Ok(EV_KEY::KEY_MACRO23), + "KEY_MACRO24" => Ok(EV_KEY::KEY_MACRO24), + "KEY_MACRO25" => Ok(EV_KEY::KEY_MACRO25), + "KEY_MACRO26" => Ok(EV_KEY::KEY_MACRO26), + "KEY_MACRO27" => Ok(EV_KEY::KEY_MACRO27), + "KEY_MACRO28" => Ok(EV_KEY::KEY_MACRO28), + "KEY_MACRO29" => Ok(EV_KEY::KEY_MACRO29), + "KEY_MACRO30" => Ok(EV_KEY::KEY_MACRO30), + "KEY_MACRO_RECORD_START" => Ok(EV_KEY::KEY_MACRO_RECORD_START), + "KEY_MACRO_RECORD_STOP" => Ok(EV_KEY::KEY_MACRO_RECORD_STOP), + "KEY_MACRO_PRESET_CYCLE" => Ok(EV_KEY::KEY_MACRO_PRESET_CYCLE), + "KEY_MACRO_PRESET1" => Ok(EV_KEY::KEY_MACRO_PRESET1), + "KEY_MACRO_PRESET2" => Ok(EV_KEY::KEY_MACRO_PRESET2), + "KEY_MACRO_PRESET3" => Ok(EV_KEY::KEY_MACRO_PRESET3), + "KEY_KBD_LCD_MENU1" => Ok(EV_KEY::KEY_KBD_LCD_MENU1), + "KEY_KBD_LCD_MENU2" => Ok(EV_KEY::KEY_KBD_LCD_MENU2), + "KEY_KBD_LCD_MENU3" => Ok(EV_KEY::KEY_KBD_LCD_MENU3), + "KEY_KBD_LCD_MENU4" => Ok(EV_KEY::KEY_KBD_LCD_MENU4), + "KEY_KBD_LCD_MENU5" => Ok(EV_KEY::KEY_KBD_LCD_MENU5), + "KEY_MAX" => Ok(EV_KEY::KEY_MAX), + "BTN_0" => Ok(EV_KEY::BTN_0), + "BTN_1" => Ok(EV_KEY::BTN_1), + "BTN_2" => Ok(EV_KEY::BTN_2), + "BTN_3" => Ok(EV_KEY::BTN_3), + "BTN_4" => Ok(EV_KEY::BTN_4), + "BTN_5" => Ok(EV_KEY::BTN_5), + "BTN_6" => Ok(EV_KEY::BTN_6), + "BTN_7" => Ok(EV_KEY::BTN_7), + "BTN_8" => Ok(EV_KEY::BTN_8), + "BTN_9" => Ok(EV_KEY::BTN_9), + "BTN_LEFT" => Ok(EV_KEY::BTN_LEFT), + "BTN_RIGHT" => Ok(EV_KEY::BTN_RIGHT), + "BTN_MIDDLE" => Ok(EV_KEY::BTN_MIDDLE), + "BTN_SIDE" => Ok(EV_KEY::BTN_SIDE), + "BTN_EXTRA" => Ok(EV_KEY::BTN_EXTRA), + "BTN_FORWARD" => Ok(EV_KEY::BTN_FORWARD), + "BTN_BACK" => Ok(EV_KEY::BTN_BACK), + "BTN_TASK" => Ok(EV_KEY::BTN_TASK), + "BTN_TRIGGER" => Ok(EV_KEY::BTN_TRIGGER), + "BTN_THUMB" => Ok(EV_KEY::BTN_THUMB), + "BTN_THUMB2" => Ok(EV_KEY::BTN_THUMB2), + "BTN_TOP" => Ok(EV_KEY::BTN_TOP), + "BTN_TOP2" => Ok(EV_KEY::BTN_TOP2), + "BTN_PINKIE" => Ok(EV_KEY::BTN_PINKIE), + "BTN_BASE" => Ok(EV_KEY::BTN_BASE), + "BTN_BASE2" => Ok(EV_KEY::BTN_BASE2), + "BTN_BASE3" => Ok(EV_KEY::BTN_BASE3), + "BTN_BASE4" => Ok(EV_KEY::BTN_BASE4), + "BTN_BASE5" => Ok(EV_KEY::BTN_BASE5), + "BTN_BASE6" => Ok(EV_KEY::BTN_BASE6), + "BTN_DEAD" => Ok(EV_KEY::BTN_DEAD), + "BTN_SOUTH" => Ok(EV_KEY::BTN_SOUTH), + "BTN_EAST" => Ok(EV_KEY::BTN_EAST), + "BTN_C" => Ok(EV_KEY::BTN_C), + "BTN_NORTH" => Ok(EV_KEY::BTN_NORTH), + "BTN_WEST" => Ok(EV_KEY::BTN_WEST), + "BTN_Z" => Ok(EV_KEY::BTN_Z), + "BTN_TL" => Ok(EV_KEY::BTN_TL), + "BTN_TR" => Ok(EV_KEY::BTN_TR), + "BTN_TL2" => Ok(EV_KEY::BTN_TL2), + "BTN_TR2" => Ok(EV_KEY::BTN_TR2), + "BTN_SELECT" => Ok(EV_KEY::BTN_SELECT), + "BTN_START" => Ok(EV_KEY::BTN_START), + "BTN_MODE" => Ok(EV_KEY::BTN_MODE), + "BTN_THUMBL" => Ok(EV_KEY::BTN_THUMBL), + "BTN_THUMBR" => Ok(EV_KEY::BTN_THUMBR), + "BTN_TOOL_PEN" => Ok(EV_KEY::BTN_TOOL_PEN), + "BTN_TOOL_RUBBER" => Ok(EV_KEY::BTN_TOOL_RUBBER), + "BTN_TOOL_BRUSH" => Ok(EV_KEY::BTN_TOOL_BRUSH), + "BTN_TOOL_PENCIL" => Ok(EV_KEY::BTN_TOOL_PENCIL), + "BTN_TOOL_AIRBRUSH" => Ok(EV_KEY::BTN_TOOL_AIRBRUSH), + "BTN_TOOL_FINGER" => Ok(EV_KEY::BTN_TOOL_FINGER), + "BTN_TOOL_MOUSE" => Ok(EV_KEY::BTN_TOOL_MOUSE), + "BTN_TOOL_LENS" => Ok(EV_KEY::BTN_TOOL_LENS), + "BTN_TOOL_QUINTTAP" => Ok(EV_KEY::BTN_TOOL_QUINTTAP), + "BTN_STYLUS3" => Ok(EV_KEY::BTN_STYLUS3), + "BTN_TOUCH" => Ok(EV_KEY::BTN_TOUCH), + "BTN_STYLUS" => Ok(EV_KEY::BTN_STYLUS), + "BTN_STYLUS2" => Ok(EV_KEY::BTN_STYLUS2), + "BTN_TOOL_DOUBLETAP" => Ok(EV_KEY::BTN_TOOL_DOUBLETAP), + "BTN_TOOL_TRIPLETAP" => Ok(EV_KEY::BTN_TOOL_TRIPLETAP), + "BTN_TOOL_QUADTAP" => Ok(EV_KEY::BTN_TOOL_QUADTAP), + "BTN_GEAR_DOWN" => Ok(EV_KEY::BTN_GEAR_DOWN), + "BTN_GEAR_UP" => Ok(EV_KEY::BTN_GEAR_UP), + "BTN_DPAD_UP" => Ok(EV_KEY::BTN_DPAD_UP), + "BTN_DPAD_DOWN" => Ok(EV_KEY::BTN_DPAD_DOWN), + "BTN_DPAD_LEFT" => Ok(EV_KEY::BTN_DPAD_LEFT), + "BTN_DPAD_RIGHT" => Ok(EV_KEY::BTN_DPAD_RIGHT), + "BTN_TRIGGER_HAPPY1" => Ok(EV_KEY::BTN_TRIGGER_HAPPY1), + "BTN_TRIGGER_HAPPY2" => Ok(EV_KEY::BTN_TRIGGER_HAPPY2), + "BTN_TRIGGER_HAPPY3" => Ok(EV_KEY::BTN_TRIGGER_HAPPY3), + "BTN_TRIGGER_HAPPY4" => Ok(EV_KEY::BTN_TRIGGER_HAPPY4), + "BTN_TRIGGER_HAPPY5" => Ok(EV_KEY::BTN_TRIGGER_HAPPY5), + "BTN_TRIGGER_HAPPY6" => Ok(EV_KEY::BTN_TRIGGER_HAPPY6), + "BTN_TRIGGER_HAPPY7" => Ok(EV_KEY::BTN_TRIGGER_HAPPY7), + "BTN_TRIGGER_HAPPY8" => Ok(EV_KEY::BTN_TRIGGER_HAPPY8), + "BTN_TRIGGER_HAPPY9" => Ok(EV_KEY::BTN_TRIGGER_HAPPY9), + "BTN_TRIGGER_HAPPY10" => Ok(EV_KEY::BTN_TRIGGER_HAPPY10), + "BTN_TRIGGER_HAPPY11" => Ok(EV_KEY::BTN_TRIGGER_HAPPY11), + "BTN_TRIGGER_HAPPY12" => Ok(EV_KEY::BTN_TRIGGER_HAPPY12), + "BTN_TRIGGER_HAPPY13" => Ok(EV_KEY::BTN_TRIGGER_HAPPY13), + "BTN_TRIGGER_HAPPY14" => Ok(EV_KEY::BTN_TRIGGER_HAPPY14), + "BTN_TRIGGER_HAPPY15" => Ok(EV_KEY::BTN_TRIGGER_HAPPY15), + "BTN_TRIGGER_HAPPY16" => Ok(EV_KEY::BTN_TRIGGER_HAPPY16), + "BTN_TRIGGER_HAPPY17" => Ok(EV_KEY::BTN_TRIGGER_HAPPY17), + "BTN_TRIGGER_HAPPY18" => Ok(EV_KEY::BTN_TRIGGER_HAPPY18), + "BTN_TRIGGER_HAPPY19" => Ok(EV_KEY::BTN_TRIGGER_HAPPY19), + "BTN_TRIGGER_HAPPY20" => Ok(EV_KEY::BTN_TRIGGER_HAPPY20), + "BTN_TRIGGER_HAPPY21" => Ok(EV_KEY::BTN_TRIGGER_HAPPY21), + "BTN_TRIGGER_HAPPY22" => Ok(EV_KEY::BTN_TRIGGER_HAPPY22), + "BTN_TRIGGER_HAPPY23" => Ok(EV_KEY::BTN_TRIGGER_HAPPY23), + "BTN_TRIGGER_HAPPY24" => Ok(EV_KEY::BTN_TRIGGER_HAPPY24), + "BTN_TRIGGER_HAPPY25" => Ok(EV_KEY::BTN_TRIGGER_HAPPY25), + "BTN_TRIGGER_HAPPY26" => Ok(EV_KEY::BTN_TRIGGER_HAPPY26), + "BTN_TRIGGER_HAPPY27" => Ok(EV_KEY::BTN_TRIGGER_HAPPY27), + "BTN_TRIGGER_HAPPY28" => Ok(EV_KEY::BTN_TRIGGER_HAPPY28), + "BTN_TRIGGER_HAPPY29" => Ok(EV_KEY::BTN_TRIGGER_HAPPY29), + "BTN_TRIGGER_HAPPY30" => Ok(EV_KEY::BTN_TRIGGER_HAPPY30), + "BTN_TRIGGER_HAPPY31" => Ok(EV_KEY::BTN_TRIGGER_HAPPY31), + "BTN_TRIGGER_HAPPY32" => Ok(EV_KEY::BTN_TRIGGER_HAPPY32), + "BTN_TRIGGER_HAPPY33" => Ok(EV_KEY::BTN_TRIGGER_HAPPY33), + "BTN_TRIGGER_HAPPY34" => Ok(EV_KEY::BTN_TRIGGER_HAPPY34), + "BTN_TRIGGER_HAPPY35" => Ok(EV_KEY::BTN_TRIGGER_HAPPY35), + "BTN_TRIGGER_HAPPY36" => Ok(EV_KEY::BTN_TRIGGER_HAPPY36), + "BTN_TRIGGER_HAPPY37" => Ok(EV_KEY::BTN_TRIGGER_HAPPY37), + "BTN_TRIGGER_HAPPY38" => Ok(EV_KEY::BTN_TRIGGER_HAPPY38), + "BTN_TRIGGER_HAPPY39" => Ok(EV_KEY::BTN_TRIGGER_HAPPY39), + "BTN_TRIGGER_HAPPY40" => Ok(EV_KEY::BTN_TRIGGER_HAPPY40), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_KEY { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_LED { + LED_NUML = 0, + LED_CAPSL = 1, + LED_SCROLLL = 2, + LED_COMPOSE = 3, + LED_KANA = 4, + LED_SLEEP = 5, + LED_SUSPEND = 6, + LED_MUTE = 7, + LED_MISC = 8, + LED_MAIL = 9, + LED_CHARGING = 10, + LED_MAX = 15, +} + +pub const fn int_to_ev_led(code: u32) -> Option { + match code { + 0 => Some(EV_LED::LED_NUML), + 1 => Some(EV_LED::LED_CAPSL), + 2 => Some(EV_LED::LED_SCROLLL), + 3 => Some(EV_LED::LED_COMPOSE), + 4 => Some(EV_LED::LED_KANA), + 5 => Some(EV_LED::LED_SLEEP), + 6 => Some(EV_LED::LED_SUSPEND), + 7 => Some(EV_LED::LED_MUTE), + 8 => Some(EV_LED::LED_MISC), + 9 => Some(EV_LED::LED_MAIL), + 10 => Some(EV_LED::LED_CHARGING), + 15 => Some(EV_LED::LED_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_LED { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "LED_NUML" => Ok(EV_LED::LED_NUML), + "LED_CAPSL" => Ok(EV_LED::LED_CAPSL), + "LED_SCROLLL" => Ok(EV_LED::LED_SCROLLL), + "LED_COMPOSE" => Ok(EV_LED::LED_COMPOSE), + "LED_KANA" => Ok(EV_LED::LED_KANA), + "LED_SLEEP" => Ok(EV_LED::LED_SLEEP), + "LED_SUSPEND" => Ok(EV_LED::LED_SUSPEND), + "LED_MUTE" => Ok(EV_LED::LED_MUTE), + "LED_MISC" => Ok(EV_LED::LED_MISC), + "LED_MAIL" => Ok(EV_LED::LED_MAIL), + "LED_CHARGING" => Ok(EV_LED::LED_CHARGING), + "LED_MAX" => Ok(EV_LED::LED_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_LED { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SND { + SND_CLICK = 0, + SND_BELL = 1, + SND_TONE = 2, + SND_MAX = 7, +} + +pub const fn int_to_ev_snd(code: u32) -> Option { + match code { + 0 => Some(EV_SND::SND_CLICK), + 1 => Some(EV_SND::SND_BELL), + 2 => Some(EV_SND::SND_TONE), + 7 => Some(EV_SND::SND_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_SND { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SND_CLICK" => Ok(EV_SND::SND_CLICK), + "SND_BELL" => Ok(EV_SND::SND_BELL), + "SND_TONE" => Ok(EV_SND::SND_TONE), + "SND_MAX" => Ok(EV_SND::SND_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SND { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_MSC { + MSC_SERIAL = 0, + MSC_PULSELED = 1, + MSC_GESTURE = 2, + MSC_RAW = 3, + MSC_SCAN = 4, + MSC_TIMESTAMP = 5, + MSC_MAX = 7, +} + +pub const fn int_to_ev_msc(code: u32) -> Option { + match code { + 0 => Some(EV_MSC::MSC_SERIAL), + 1 => Some(EV_MSC::MSC_PULSELED), + 2 => Some(EV_MSC::MSC_GESTURE), + 3 => Some(EV_MSC::MSC_RAW), + 4 => Some(EV_MSC::MSC_SCAN), + 5 => Some(EV_MSC::MSC_TIMESTAMP), + 7 => Some(EV_MSC::MSC_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_MSC { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "MSC_SERIAL" => Ok(EV_MSC::MSC_SERIAL), + "MSC_PULSELED" => Ok(EV_MSC::MSC_PULSELED), + "MSC_GESTURE" => Ok(EV_MSC::MSC_GESTURE), + "MSC_RAW" => Ok(EV_MSC::MSC_RAW), + "MSC_SCAN" => Ok(EV_MSC::MSC_SCAN), + "MSC_TIMESTAMP" => Ok(EV_MSC::MSC_TIMESTAMP), + "MSC_MAX" => Ok(EV_MSC::MSC_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_MSC { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SW { + SW_LID = 0, + SW_TABLET_MODE = 1, + SW_HEADPHONE_INSERT = 2, + SW_RFKILL_ALL = 3, + SW_MICROPHONE_INSERT = 4, + SW_DOCK = 5, + SW_LINEOUT_INSERT = 6, + SW_JACK_PHYSICAL_INSERT = 7, + SW_VIDEOOUT_INSERT = 8, + SW_CAMERA_LENS_COVER = 9, + SW_KEYPAD_SLIDE = 10, + SW_FRONT_PROXIMITY = 11, + SW_ROTATE_LOCK = 12, + SW_LINEIN_INSERT = 13, + SW_MUTE_DEVICE = 14, + SW_PEN_INSERTED = 15, + SW_MACHINE_COVER = 16, +} + +impl EV_SW { + pub const SW_MAX: EV_SW = EV_SW::SW_MACHINE_COVER; +} + +pub const fn int_to_ev_sw(code: u32) -> Option { + match code { + 0 => Some(EV_SW::SW_LID), + 1 => Some(EV_SW::SW_TABLET_MODE), + 2 => Some(EV_SW::SW_HEADPHONE_INSERT), + 3 => Some(EV_SW::SW_RFKILL_ALL), + 4 => Some(EV_SW::SW_MICROPHONE_INSERT), + 5 => Some(EV_SW::SW_DOCK), + 6 => Some(EV_SW::SW_LINEOUT_INSERT), + 7 => Some(EV_SW::SW_JACK_PHYSICAL_INSERT), + 8 => Some(EV_SW::SW_VIDEOOUT_INSERT), + 9 => Some(EV_SW::SW_CAMERA_LENS_COVER), + 10 => Some(EV_SW::SW_KEYPAD_SLIDE), + 11 => Some(EV_SW::SW_FRONT_PROXIMITY), + 12 => Some(EV_SW::SW_ROTATE_LOCK), + 13 => Some(EV_SW::SW_LINEIN_INSERT), + 14 => Some(EV_SW::SW_MUTE_DEVICE), + 15 => Some(EV_SW::SW_PEN_INSERTED), + 16 => Some(EV_SW::SW_MACHINE_COVER), + _ => None, + } +} + +impl std::str::FromStr for EV_SW { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SW_LID" => Ok(EV_SW::SW_LID), + "SW_TABLET_MODE" => Ok(EV_SW::SW_TABLET_MODE), + "SW_HEADPHONE_INSERT" => Ok(EV_SW::SW_HEADPHONE_INSERT), + "SW_RFKILL_ALL" => Ok(EV_SW::SW_RFKILL_ALL), + "SW_MICROPHONE_INSERT" => Ok(EV_SW::SW_MICROPHONE_INSERT), + "SW_DOCK" => Ok(EV_SW::SW_DOCK), + "SW_LINEOUT_INSERT" => Ok(EV_SW::SW_LINEOUT_INSERT), + "SW_JACK_PHYSICAL_INSERT" => Ok(EV_SW::SW_JACK_PHYSICAL_INSERT), + "SW_VIDEOOUT_INSERT" => Ok(EV_SW::SW_VIDEOOUT_INSERT), + "SW_CAMERA_LENS_COVER" => Ok(EV_SW::SW_CAMERA_LENS_COVER), + "SW_KEYPAD_SLIDE" => Ok(EV_SW::SW_KEYPAD_SLIDE), + "SW_FRONT_PROXIMITY" => Ok(EV_SW::SW_FRONT_PROXIMITY), + "SW_ROTATE_LOCK" => Ok(EV_SW::SW_ROTATE_LOCK), + "SW_LINEIN_INSERT" => Ok(EV_SW::SW_LINEIN_INSERT), + "SW_MUTE_DEVICE" => Ok(EV_SW::SW_MUTE_DEVICE), + "SW_PEN_INSERTED" => Ok(EV_SW::SW_PEN_INSERTED), + "SW_MACHINE_COVER" => Ok(EV_SW::SW_MACHINE_COVER), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SW { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_SYN { + SYN_REPORT = 0, + SYN_CONFIG = 1, + SYN_MT_REPORT = 2, + SYN_DROPPED = 3, + SYN_MAX = 15, +} + +pub const fn int_to_ev_syn(code: u32) -> Option { + match code { + 0 => Some(EV_SYN::SYN_REPORT), + 1 => Some(EV_SYN::SYN_CONFIG), + 2 => Some(EV_SYN::SYN_MT_REPORT), + 3 => Some(EV_SYN::SYN_DROPPED), + 15 => Some(EV_SYN::SYN_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_SYN { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "SYN_REPORT" => Ok(EV_SYN::SYN_REPORT), + "SYN_CONFIG" => Ok(EV_SYN::SYN_CONFIG), + "SYN_MT_REPORT" => Ok(EV_SYN::SYN_MT_REPORT), + "SYN_DROPPED" => Ok(EV_SYN::SYN_DROPPED), + "SYN_MAX" => Ok(EV_SYN::SYN_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_SYN { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_REP { + REP_DELAY = 0, + REP_PERIOD = 1, +} + +impl EV_REP { + pub const REP_MAX: EV_REP = EV_REP::REP_PERIOD; +} + +pub const fn int_to_ev_rep(code: u32) -> Option { + match code { + 0 => Some(EV_REP::REP_DELAY), + 1 => Some(EV_REP::REP_PERIOD), + _ => None, + } +} + +impl std::str::FromStr for EV_REP { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "REP_DELAY" => Ok(EV_REP::REP_DELAY), + "REP_PERIOD" => Ok(EV_REP::REP_PERIOD), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_REP { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum InputProp { + INPUT_PROP_POINTER = 0, + INPUT_PROP_DIRECT = 1, + INPUT_PROP_BUTTONPAD = 2, + INPUT_PROP_SEMI_MT = 3, + INPUT_PROP_TOPBUTTONPAD = 4, + INPUT_PROP_POINTING_STICK = 5, + INPUT_PROP_ACCELEROMETER = 6, + INPUT_PROP_MAX = 31, +} + +pub const fn int_to_input_prop(code: u32) -> Option { + match code { + 0 => Some(InputProp::INPUT_PROP_POINTER), + 1 => Some(InputProp::INPUT_PROP_DIRECT), + 2 => Some(InputProp::INPUT_PROP_BUTTONPAD), + 3 => Some(InputProp::INPUT_PROP_SEMI_MT), + 4 => Some(InputProp::INPUT_PROP_TOPBUTTONPAD), + 5 => Some(InputProp::INPUT_PROP_POINTING_STICK), + 6 => Some(InputProp::INPUT_PROP_ACCELEROMETER), + 31 => Some(InputProp::INPUT_PROP_MAX), + _ => None, + } +} + +impl std::str::FromStr for InputProp { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "INPUT_PROP_POINTER" => Ok(InputProp::INPUT_PROP_POINTER), + "INPUT_PROP_DIRECT" => Ok(InputProp::INPUT_PROP_DIRECT), + "INPUT_PROP_BUTTONPAD" => Ok(InputProp::INPUT_PROP_BUTTONPAD), + "INPUT_PROP_SEMI_MT" => Ok(InputProp::INPUT_PROP_SEMI_MT), + "INPUT_PROP_TOPBUTTONPAD" => Ok(InputProp::INPUT_PROP_TOPBUTTONPAD), + "INPUT_PROP_POINTING_STICK" => Ok(InputProp::INPUT_PROP_POINTING_STICK), + "INPUT_PROP_ACCELEROMETER" => Ok(InputProp::INPUT_PROP_ACCELEROMETER), + "INPUT_PROP_MAX" => Ok(InputProp::INPUT_PROP_MAX), + _ => Err(()), + } + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum EV_FF { + FF_STATUS_STOPPED = 0, + FF_STATUS_PLAYING = 1, + FF_RUMBLE = 80, + FF_PERIODIC = 81, + FF_CONSTANT = 82, + FF_SPRING = 83, + FF_FRICTION = 84, + FF_DAMPER = 85, + FF_INERTIA = 86, + FF_RAMP = 87, + FF_SQUARE = 88, + FF_TRIANGLE = 89, + FF_SINE = 90, + FF_SAW_UP = 91, + FF_SAW_DOWN = 92, + FF_CUSTOM = 93, + FF_GAIN = 96, + FF_AUTOCENTER = 97, + FF_MAX = 127, +} + +impl EV_FF { + pub const FF_STATUS_MAX: EV_FF = EV_FF::FF_STATUS_PLAYING; +} + +pub const fn int_to_ev_ff(code: u32) -> Option { + match code { + 0 => Some(EV_FF::FF_STATUS_STOPPED), + 1 => Some(EV_FF::FF_STATUS_PLAYING), + 80 => Some(EV_FF::FF_RUMBLE), + 81 => Some(EV_FF::FF_PERIODIC), + 82 => Some(EV_FF::FF_CONSTANT), + 83 => Some(EV_FF::FF_SPRING), + 84 => Some(EV_FF::FF_FRICTION), + 85 => Some(EV_FF::FF_DAMPER), + 86 => Some(EV_FF::FF_INERTIA), + 87 => Some(EV_FF::FF_RAMP), + 88 => Some(EV_FF::FF_SQUARE), + 89 => Some(EV_FF::FF_TRIANGLE), + 90 => Some(EV_FF::FF_SINE), + 91 => Some(EV_FF::FF_SAW_UP), + 92 => Some(EV_FF::FF_SAW_DOWN), + 93 => Some(EV_FF::FF_CUSTOM), + 96 => Some(EV_FF::FF_GAIN), + 97 => Some(EV_FF::FF_AUTOCENTER), + 127 => Some(EV_FF::FF_MAX), + _ => None, + } +} + +impl std::str::FromStr for EV_FF { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "FF_STATUS_STOPPED" => Ok(EV_FF::FF_STATUS_STOPPED), + "FF_STATUS_PLAYING" => Ok(EV_FF::FF_STATUS_PLAYING), + "FF_RUMBLE" => Ok(EV_FF::FF_RUMBLE), + "FF_PERIODIC" => Ok(EV_FF::FF_PERIODIC), + "FF_CONSTANT" => Ok(EV_FF::FF_CONSTANT), + "FF_SPRING" => Ok(EV_FF::FF_SPRING), + "FF_FRICTION" => Ok(EV_FF::FF_FRICTION), + "FF_DAMPER" => Ok(EV_FF::FF_DAMPER), + "FF_INERTIA" => Ok(EV_FF::FF_INERTIA), + "FF_RAMP" => Ok(EV_FF::FF_RAMP), + "FF_SQUARE" => Ok(EV_FF::FF_SQUARE), + "FF_TRIANGLE" => Ok(EV_FF::FF_TRIANGLE), + "FF_SINE" => Ok(EV_FF::FF_SINE), + "FF_SAW_UP" => Ok(EV_FF::FF_SAW_UP), + "FF_SAW_DOWN" => Ok(EV_FF::FF_SAW_DOWN), + "FF_CUSTOM" => Ok(EV_FF::FF_CUSTOM), + "FF_GAIN" => Ok(EV_FF::FF_GAIN), + "FF_AUTOCENTER" => Ok(EV_FF::FF_AUTOCENTER), + "FF_MAX" => Ok(EV_FF::FF_MAX), + _ => Err(()), + } + } +} + +impl std::fmt::Display for EV_FF { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} + +#[allow(non_camel_case_types)] +#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, PartialOrd, Ord)] +pub enum BusType { + BUS_PCI = 1, + BUS_ISAPNP = 2, + BUS_USB = 3, + BUS_HIL = 4, + BUS_BLUETOOTH = 5, + BUS_VIRTUAL = 6, + BUS_ISA = 16, + BUS_I8042 = 17, + BUS_XTKBD = 18, + BUS_RS232 = 19, + BUS_GAMEPORT = 20, + BUS_PARPORT = 21, + BUS_AMIGA = 22, + BUS_ADB = 23, + BUS_I2C = 24, + BUS_HOST = 25, + BUS_GSC = 26, + BUS_ATARI = 27, + BUS_SPI = 28, + BUS_RMI = 29, + BUS_CEC = 30, + BUS_INTEL_ISHTP = 31, + BUS_AMD_SFH = 32, +} + +pub const fn int_to_bus_type(code: u32) -> Option { + match code { + 1 => Some(BusType::BUS_PCI), + 2 => Some(BusType::BUS_ISAPNP), + 3 => Some(BusType::BUS_USB), + 4 => Some(BusType::BUS_HIL), + 5 => Some(BusType::BUS_BLUETOOTH), + 6 => Some(BusType::BUS_VIRTUAL), + 16 => Some(BusType::BUS_ISA), + 17 => Some(BusType::BUS_I8042), + 18 => Some(BusType::BUS_XTKBD), + 19 => Some(BusType::BUS_RS232), + 20 => Some(BusType::BUS_GAMEPORT), + 21 => Some(BusType::BUS_PARPORT), + 22 => Some(BusType::BUS_AMIGA), + 23 => Some(BusType::BUS_ADB), + 24 => Some(BusType::BUS_I2C), + 25 => Some(BusType::BUS_HOST), + 26 => Some(BusType::BUS_GSC), + 27 => Some(BusType::BUS_ATARI), + 28 => Some(BusType::BUS_SPI), + 29 => Some(BusType::BUS_RMI), + 30 => Some(BusType::BUS_CEC), + 31 => Some(BusType::BUS_INTEL_ISHTP), + 32 => Some(BusType::BUS_AMD_SFH), + _ => None, + } +} + +impl std::str::FromStr for BusType { + type Err = (); + fn from_str(s: &str) -> Result { + match s { + "BUS_PCI" => Ok(BusType::BUS_PCI), + "BUS_ISAPNP" => Ok(BusType::BUS_ISAPNP), + "BUS_USB" => Ok(BusType::BUS_USB), + "BUS_HIL" => Ok(BusType::BUS_HIL), + "BUS_BLUETOOTH" => Ok(BusType::BUS_BLUETOOTH), + "BUS_VIRTUAL" => Ok(BusType::BUS_VIRTUAL), + "BUS_ISA" => Ok(BusType::BUS_ISA), + "BUS_I8042" => Ok(BusType::BUS_I8042), + "BUS_XTKBD" => Ok(BusType::BUS_XTKBD), + "BUS_RS232" => Ok(BusType::BUS_RS232), + "BUS_GAMEPORT" => Ok(BusType::BUS_GAMEPORT), + "BUS_PARPORT" => Ok(BusType::BUS_PARPORT), + "BUS_AMIGA" => Ok(BusType::BUS_AMIGA), + "BUS_ADB" => Ok(BusType::BUS_ADB), + "BUS_I2C" => Ok(BusType::BUS_I2C), + "BUS_HOST" => Ok(BusType::BUS_HOST), + "BUS_GSC" => Ok(BusType::BUS_GSC), + "BUS_ATARI" => Ok(BusType::BUS_ATARI), + "BUS_SPI" => Ok(BusType::BUS_SPI), + "BUS_RMI" => Ok(BusType::BUS_RMI), + "BUS_CEC" => Ok(BusType::BUS_CEC), + "BUS_INTEL_ISHTP" => Ok(BusType::BUS_INTEL_ISHTP), + "BUS_AMD_SFH" => Ok(BusType::BUS_AMD_SFH), + _ => Err(()), + } + } +} + +impl std::fmt::Display for BusType { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + write!(f, "{:?}", self) + } +} diff --git a/evdev/src/main/rust/evdev/src/lib.rs b/evdev/src/main/rust/evdev/src/lib.rs new file mode 100644 index 0000000000..d2ed7a9196 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/lib.rs @@ -0,0 +1,238 @@ +//! This crate is from evdev_rs https://github.com/ndesh26/evdev-rs. +//! It is copied so I can compile libevdev against the Android NDK and so that +//! a libevdev submodule isn't required. Also so more complex build steps can be removed. +//! Rust bindings to libevdev, a wrapper for evdev devices. + +#![allow(unused_imports, dead_code, unused_assignments)] + +#[macro_use] +mod macros; + +mod device; +pub mod enums; +mod libevdev; +mod uinput; +pub mod util; + +use bitflags::bitflags; +use libc::{c_uint, suseconds_t, time_t}; +use std::convert::{TryFrom, TryInto}; +use std::time::{Duration, SystemTime, SystemTimeError, UNIX_EPOCH}; + +use enums::*; +use util::*; + +pub use util::EventCodeIterator; +pub use util::EventTypeIterator; +pub use util::InputPropIterator; + +#[doc(inline)] +pub use device::Device; +#[doc(inline)] +pub use device::DeviceWrapper; +#[doc(inline)] +pub use device::Enable; +#[doc(inline)] +pub use device::EnableCodeData; +#[doc(inline)] +pub use device::UninitDevice; +#[doc(inline)] +pub use uinput::UInputDevice; + +pub enum GrabMode { + /// Grab the device if not currently grabbed + Grab = libevdev::LIBEVDEV_GRAB as isize, + /// Ungrab the device if currently grabbed + Ungrab = libevdev::LIBEVDEV_UNGRAB as isize, +} + +bitflags! { +#[derive(PartialEq, Eq, PartialOrd, Ord, Hash, Debug, Clone, Copy)] + pub struct ReadFlag: u32 { + /// Process data in sync mode + const SYNC = 1; + /// Process data in normal mode + const NORMAL = 2; + /// Pretend the next event is a SYN_DROPPED and require the + /// caller to sync + const FORCE_SYNC = 4; + /// The fd is not in O_NONBLOCK and a read may block + const BLOCKING = 8; + } +} + +#[derive(PartialEq)] +pub enum ReadStatus { + /// `next_event` has finished without an error and an event is available + /// for processing. + Success = libevdev::LIBEVDEV_READ_STATUS_SUCCESS as isize, + /// Depending on the `next_event` read flag: + /// libevdev received a SYN_DROPPED from the device, and the caller should + /// now resync the device, or, an event has been read in sync mode. + Sync = libevdev::LIBEVDEV_READ_STATUS_SYNC as isize, +} + +pub enum LedState { + /// Turn the LED on + On = libevdev::LIBEVDEV_LED_ON as isize, + /// Turn the LED off + Off = libevdev::LIBEVDEV_LED_OFF as isize, +} + +#[derive(Debug)] +pub struct DeviceId { + pub bustype: BusType, + pub vendor: u16, + pub product: u16, + pub version: u16, +} + +#[derive(Clone, Copy, Debug)] +/// used by EVIOCGABS/EVIOCSABS ioctls +pub struct AbsInfo { + /// latest reported value for the axis + pub value: i32, + /// specifies minimum value for the axis + pub minimum: i32, + /// specifies maximum value for the axis + pub maximum: i32, + /// specifies fuzz value that is used to filter noise from + /// the event stream + pub fuzz: i32, + /// values that are within this value will be discarded by + /// joydev interface and reported as 0 instead + pub flat: i32, + /// specifies resolution for the values reported for + /// the axis + pub resolution: i32, +} + +impl AbsInfo { + pub const fn from_raw(absinfo: libevdev::input_absinfo) -> AbsInfo { + AbsInfo { + value: absinfo.value, + minimum: absinfo.minimum, + maximum: absinfo.maximum, + fuzz: absinfo.fuzz, + flat: absinfo.flat, + resolution: absinfo.resolution, + } + } + + pub const fn as_raw(&self) -> libevdev::input_absinfo { + libevdev::input_absinfo { + value: self.value, + minimum: self.minimum, + maximum: self.maximum, + fuzz: self.fuzz, + flat: self.flat, + resolution: self.resolution, + } + } +} + +#[derive(Copy, Clone, Eq, Hash, PartialOrd, Ord, Debug, PartialEq)] +pub struct TimeVal { + pub tv_sec: time_t, + pub tv_usec: suseconds_t, +} + +impl TryFrom for TimeVal { + type Error = SystemTimeError; + fn try_from(system_time: SystemTime) -> Result { + let d = system_time.duration_since(UNIX_EPOCH)?; + Ok(TimeVal { + tv_sec: d.as_secs() as time_t, + tv_usec: d.subsec_micros() as suseconds_t, + }) + } +} + +impl TryInto for TimeVal { + type Error = (); + /// Fails if TimeVal.tv_usec is >= 10^6 or if the TimeVal is outside + /// the range of SystemTime + fn try_into(self) -> Result { + let secs = self.tv_sec.try_into().map_err(|_| ())?; + let nanos = (self.tv_usec * 1000).try_into().map_err(|_| ())?; + let duration = Duration::new(secs, nanos); + UNIX_EPOCH.checked_add(duration).ok_or(()) + } +} + +impl TimeVal { + pub const fn new(tv_sec: time_t, tv_usec: suseconds_t) -> TimeVal { + const MICROS_PER_SEC: suseconds_t = 1_000_000; + TimeVal { + tv_sec: tv_sec + (tv_usec / MICROS_PER_SEC) as time_t, + tv_usec: tv_usec % MICROS_PER_SEC, + } + } + + pub const fn from_raw(timeval: &libc::timeval) -> TimeVal { + TimeVal { + tv_sec: timeval.tv_sec, + tv_usec: timeval.tv_usec, + } + } + + pub const fn as_raw(&self) -> libc::timeval { + libc::timeval { + tv_sec: self.tv_sec, + tv_usec: self.tv_usec, + } + } +} + +/// The event structure itself +#[derive(Clone, Debug, PartialEq, Eq, Hash)] +pub struct InputEvent { + /// The time at which event occurred + pub time: TimeVal, + pub event_code: EventCode, + pub value: i32, +} + +impl InputEvent { + pub const fn new(timeval: &TimeVal, code: &EventCode, value: i32) -> InputEvent { + InputEvent { + time: *timeval, + event_code: *code, + value, + } + } + + pub fn event_type(&self) -> Option { + int_to_event_type(event_code_to_int(&self.event_code).0) + } + + pub fn from_raw(event: &libevdev::input_event) -> InputEvent { + let ev_type = event.type_ as u32; + let event_code = int_to_event_code(ev_type, event.code as u32); + InputEvent { + time: TimeVal::from_raw(&event.time), + event_code, + value: event.value, + } + } + + pub fn as_raw(&self) -> libevdev::input_event { + let (ev_type, ev_code) = event_code_to_int(&self.event_code); + libevdev::input_event { + time: self.time.as_raw(), + type_: ev_type as u16, + code: ev_code as u16, + value: self.value, + } + } + + pub fn is_type(&self, ev_type: &EventType) -> bool { + unsafe { libevdev::libevdev_event_is_type(&self.as_raw(), *ev_type as c_uint) == 1 } + } + + pub fn is_code(&self, code: &EventCode) -> bool { + let (ev_type, ev_code) = event_code_to_int(code); + + unsafe { libevdev::libevdev_event_is_code(&self.as_raw(), ev_type, ev_code) == 1 } + } +} diff --git a/evdev/src/main/rust/evdev/src/libevdev.rs b/evdev/src/main/rust/evdev/src/libevdev.rs new file mode 100644 index 0000000000..caa6a7ab79 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/libevdev.rs @@ -0,0 +1,187 @@ +#![allow(bad_style)] +#![allow(dead_code)] +#![allow(improper_ctypes)] + +pub use libc::timeval; +use libc::{c_char, c_int, c_uint, c_ushort, c_void, size_t}; + +pub type __enum_ty = c_int; +pub type libevdev_read_flag = __enum_ty; +pub type libevdev_log_priority = __enum_ty; +pub type libevdev_grab_mode = __enum_ty; +pub type libevdev_read_status = __enum_ty; +pub type libevdev_led_value = __enum_ty; +pub type libevdev_uinput_open_mode = __enum_ty; + +pub const LIBEVDEV_READ_FLAG_SYNC: libevdev_read_flag = 1; +pub const LIBEVDEV_READ_FLAG_NORMAL: libevdev_read_flag = 2; +pub const LIBEVDEV_READ_FLAG_FORCE_SYNC: libevdev_read_flag = 4; +pub const LIBEVDEV_READ_FLAG_BLOCKING: libevdev_read_flag = 8; + +pub const LIBEVDEV_LOG_ERROR: libevdev_log_priority = 10; +pub const LIBEVDEV_LOG_INFO: libevdev_log_priority = 20; +pub const LIBEVDEV_LOG_DEBUG: libevdev_log_priority = 30; + +pub const LIBEVDEV_GRAB: libevdev_grab_mode = 3; +pub const LIBEVDEV_UNGRAB: libevdev_grab_mode = 4; + +pub const LIBEVDEV_READ_STATUS_SUCCESS: libevdev_read_status = 0; +pub const LIBEVDEV_READ_STATUS_SYNC: libevdev_read_status = 1; + +pub const LIBEVDEV_LED_ON: libevdev_led_value = 3; +pub const LIBEVDEV_LED_OFF: libevdev_led_value = 4; + +pub const LIBEVDEV_UINPUT_OPEN_MANAGED: libevdev_uinput_open_mode = -2; + +pub enum libevdev {} + +pub enum libevdev_uinput {} + +#[repr(C)] +#[derive(Copy, Clone)] +pub struct input_event { + pub time: timeval, + pub type_: c_ushort, + pub code: c_ushort, + pub value: c_int, +} + +#[repr(C)] +#[derive(Debug, Copy, Clone)] +pub struct input_absinfo { + pub value: c_int, + pub minimum: c_int, + pub maximum: c_int, + pub fuzz: c_int, + pub flat: c_int, + pub resolution: c_int, +} + +extern "C" { + pub fn libevdev_new() -> *mut libevdev; + pub fn libevdev_new_from_fd(fd: c_int, ctx: *mut *mut libevdev) -> c_int; + pub fn libevdev_free(ctx: *mut libevdev); + pub fn libevdev_grab(ctx: *mut libevdev, grab: libevdev_grab_mode) -> c_int; + pub fn libevdev_set_fd(ctx: *mut libevdev, fd: c_int) -> c_int; + pub fn libevdev_change_fd(ctx: *mut libevdev, fd: c_int) -> c_int; + pub fn libevdev_get_fd(ctx: *mut libevdev) -> c_int; + pub fn libevdev_next_event(ctx: *mut libevdev, flags: c_uint, ev: *mut input_event) -> c_int; + pub fn libevdev_has_event_pending(ctx: *mut libevdev) -> c_int; + pub fn libevdev_get_name(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_name(ctx: *mut libevdev, name: *const c_char); + pub fn libevdev_get_phys(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_phys(ctx: *mut libevdev, phys: *const c_char); + pub fn libevdev_get_uniq(ctx: *const libevdev) -> *const c_char; + pub fn libevdev_set_uniq(ctx: *mut libevdev, uniq: *const c_char); + pub fn libevdev_get_id_product(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_product(ctx: *mut libevdev, product_id: c_int); + pub fn libevdev_get_id_vendor(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_vendor(ctx: *mut libevdev, vendor_id: c_int); + pub fn libevdev_get_id_bustype(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_bustype(ctx: *mut libevdev, bustype: c_int); + pub fn libevdev_get_id_version(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_id_version(ctx: *mut libevdev, version: c_int); + pub fn libevdev_get_driver_version(ctx: *const libevdev) -> c_int; + pub fn libevdev_has_property(ctx: *const libevdev, prop: c_uint) -> c_int; + pub fn libevdev_enable_property(ctx: *mut libevdev, prop: c_uint) -> c_int; + pub fn libevdev_disable_property(ctx: *mut libevdev, prop: c_uint) -> c_int; + pub fn libevdev_has_event_type(ctx: *const libevdev, type_: c_uint) -> c_int; + pub fn libevdev_has_event_code(ctx: *const libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_get_abs_minimum(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_maximum(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_fuzz(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_flat(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_resolution(ctx: *const libevdev, code: c_uint) -> c_int; + pub fn libevdev_get_abs_info(ctx: *const libevdev, code: c_uint) -> *const input_absinfo; + pub fn libevdev_get_event_value(ctx: *const libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_set_event_value( + ctx: *mut libevdev, + type_: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; + pub fn libevdev_fetch_event_value( + ctx: *const libevdev, + type_: c_uint, + code: c_uint, + value: *mut c_int, + ) -> c_int; + pub fn libevdev_get_slot_value(ctx: *const libevdev, slot: c_uint, code: c_uint) -> c_int; + pub fn libevdev_set_slot_value( + ctx: *mut libevdev, + slot: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; + pub fn libevdev_fetch_slot_value( + ctx: *const libevdev, + slot: c_uint, + code: c_uint, + value: *mut c_int, + ) -> c_int; + pub fn libevdev_get_num_slots(ctx: *const libevdev) -> c_int; + pub fn libevdev_get_current_slot(ctx: *const libevdev) -> c_int; + pub fn libevdev_set_abs_minimum(ctx: *mut libevdev, code: c_uint, min: c_int); + pub fn libevdev_set_abs_maximum(ctx: *mut libevdev, code: c_uint, max: c_int); + pub fn libevdev_set_abs_fuzz(ctx: *mut libevdev, code: c_uint, fuzz: c_int); + pub fn libevdev_set_abs_flat(ctx: *mut libevdev, code: c_uint, flat: c_int); + pub fn libevdev_set_abs_resolution(ctx: *mut libevdev, code: c_uint, resolution: c_int); + pub fn libevdev_set_abs_info(ctx: *mut libevdev, code: c_uint, abs: *const input_absinfo); + pub fn libevdev_enable_event_type(ctx: *mut libevdev, type_: c_uint) -> c_int; + pub fn libevdev_disable_event_type(ctx: *mut libevdev, type_: c_uint) -> c_int; + pub fn libevdev_enable_event_code( + ctx: *mut libevdev, + type_: c_uint, + code: c_uint, + data: *const c_void, + ) -> c_int; + pub fn libevdev_disable_event_code(ctx: *mut libevdev, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_kernel_set_abs_info( + ctx: *mut libevdev, + code: c_uint, + abs: *const input_absinfo, + ) -> c_int; + pub fn libevdev_kernel_set_led_value( + ctx: *mut libevdev, + code: c_uint, + value: libevdev_led_value, + ) -> c_int; + pub fn libevdev_kernel_set_led_values(ctx: *mut libevdev, ...) -> c_int; + pub fn libevdev_set_clock_id(ctx: *mut libevdev, clockid: c_int) -> c_int; + pub fn libevdev_event_is_type(ev: *const input_event, type_: c_uint) -> c_int; + pub fn libevdev_event_is_code(ev: *const input_event, type_: c_uint, code: c_uint) -> c_int; + pub fn libevdev_event_type_get_name(type_: c_uint) -> *const c_char; + pub fn libevdev_event_code_get_name(type_: c_uint, code: c_uint) -> *const c_char; + pub fn libevdev_property_get_name(prop: c_uint) -> *const c_char; + pub fn libevdev_event_type_get_max(type_: c_uint) -> c_int; + pub fn libevdev_event_type_from_name(name: *const c_char) -> c_int; + pub fn libevdev_event_type_from_name_n(name: *const c_char, len: size_t) -> c_int; + pub fn libevdev_event_code_from_name(type_: c_uint, name: *const c_char) -> c_int; + pub fn libevdev_event_code_from_name_n( + type_: c_uint, + name: *const c_char, + len: size_t, + ) -> c_int; + pub fn libevdev_property_from_name(name: *const c_char) -> c_int; + pub fn libevdev_property_from_name_n(name: *const c_char, len: size_t) -> c_int; + pub fn libevdev_get_repeat( + ctx: *const libevdev, + delay: *mut c_int, + period: *mut c_int, + ) -> c_int; + pub fn libevdev_uinput_create_from_device( + ctx: *const libevdev, + uinput_fd: c_int, + uinput_dev: *mut *mut libevdev_uinput, + ) -> c_int; + pub fn libevdev_uinput_destroy(uinput_dev: *mut libevdev_uinput); + pub fn libevdev_uinput_get_devnode(uinput_dev: *mut libevdev_uinput) -> *const c_char; + pub fn libevdev_uinput_get_fd(uinput_dev: *const libevdev_uinput) -> c_int; + pub fn libevdev_uinput_get_syspath(uinput_dev: *mut libevdev_uinput) -> *const c_char; + pub fn libevdev_uinput_write_event( + uinput_dev: *const libevdev_uinput, + type_: c_uint, + code: c_uint, + value: c_int, + ) -> c_int; +} diff --git a/evdev/src/main/rust/evdev/src/macros.rs b/evdev/src/main/rust/evdev/src/macros.rs new file mode 100644 index 0000000000..27bc575fed --- /dev/null +++ b/evdev/src/main/rust/evdev/src/macros.rs @@ -0,0 +1,81 @@ +macro_rules! string_getter { + ( $( #[$doc:meta], $func_name:ident, $c_func: ident ),* ) => { + $( + #[$doc] + fn $func_name (&self) -> Option<&str> { + unsafe { + ptr_to_str(libevdev::$c_func(self.raw())) + } + } + )* + }; +} + +macro_rules! string_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, field: &str) { + let field = CString::new(field).unwrap(); + unsafe { + libevdev::$c_func(self.raw(), field.as_ptr()) + } + } + )* + }; +} + +macro_rules! product_getter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self) -> u16 { + unsafe { + libevdev::$c_func(self.raw()) as u16 + } + } + )* + }; +} + +macro_rules! product_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, field: u16) { + unsafe { + libevdev::$c_func(self.raw(), field as c_int); + } + } + )* + }; +} + +macro_rules! abs_getter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, + code: u32) -> std::io::Result { + let result = unsafe { + libevdev::$c_func(self.raw(), code as c_uint) as i32 + }; + + match result { + 0 => Err(std::io::Error::from_raw_os_error(0)), + k => Ok(k) + } + } + )* + }; +} + +macro_rules! abs_setter { + ( $( $func_name:ident, $c_func: ident ),* ) => { + $( + fn $func_name (&self, + code: u32, + val: i32) { + unsafe { + libevdev::$c_func(self.raw(), code as c_uint, val as c_int); + } + } + )* + }; +} \ No newline at end of file diff --git a/evdev/src/main/rust/evdev/src/uinput.rs b/evdev/src/main/rust/evdev/src/uinput.rs new file mode 100644 index 0000000000..f871b16692 --- /dev/null +++ b/evdev/src/main/rust/evdev/src/uinput.rs @@ -0,0 +1,116 @@ +use crate::enums::{EventCode, EventType, EV_SYN}; +use crate::util::*; +use crate::{device::DeviceWrapper, InputEvent}; +use libc::{c_int, c_uint}; +use std::io; +use std::os::unix::io::RawFd; + +use crate::libevdev; + +/// Opaque struct representing an evdev uinput device +pub struct UInputDevice { + raw: *mut libevdev::libevdev_uinput, +} + +unsafe impl Sync for UInputDevice {} +unsafe impl Send for UInputDevice {} + +impl UInputDevice { + fn raw(&self) -> *mut libevdev::libevdev_uinput { + self.raw + } + + /// Create a uinput device based on the given libevdev device. + /// + /// The uinput device will be an exact copy of the libevdev device, minus + /// the bits that uinput doesn't allow to be set. + pub fn create_from_device(device: &T) -> io::Result { + let mut libevdev_uinput = std::ptr::null_mut(); + let result = unsafe { + libevdev::libevdev_uinput_create_from_device( + device.raw(), + libevdev::LIBEVDEV_UINPUT_OPEN_MANAGED, + &mut libevdev_uinput, + ) + }; + + match result { + 0 => Ok(UInputDevice { + raw: libevdev_uinput, + }), + error => Err(io::Error::from_raw_os_error(-error)), + } + } + + ///Return the device node representing this uinput device. + /// + /// This relies on `libevdev_uinput_get_syspath()` to provide a valid syspath. + pub fn devnode(&self) -> Option<&str> { + unsafe { ptr_to_str(libevdev::libevdev_uinput_get_devnode(self.raw())) } + } + + ///Return the syspath representing this uinput device. + /// + /// If the UI_GET_SYSNAME ioctl not available, libevdev makes an educated + /// guess. The UI_GET_SYSNAME ioctl is available since Linux 3.15. + /// + /// The syspath returned is the one of the input node itself + /// (e.g. /sys/devices/virtual/input/input123), not the syspath of the + /// device node returned with libevdev_uinput_get_devnode(). + pub fn syspath(&self) -> Option<&str> { + unsafe { ptr_to_str(libevdev::libevdev_uinput_get_syspath(self.raw())) } + } + + /// Return the file descriptor used to create this uinput device. + /// + /// This is the fd pointing to /dev/uinput. This file descriptor may be used + /// to write events that are emitted by the uinput device. Closing this file + /// descriptor will destroy the uinput device. + pub fn as_fd(&self) -> Option { + match unsafe { libevdev::libevdev_uinput_get_fd(self.raw()) } { + 0 => None, + result => Some(result), + } + } + + #[deprecated( + since = "0.5.0", + note = "Prefer `as_fd`. Some function names were changed so they + more closely match their type signature. See issue 42 for discussion + https://github.com/ndesh26/evdev-rs/issues/42" + )] + pub fn fd(&self) -> Option { + self.as_fd() + } + + /// Post an event through the uinput device. + /// + /// It is the caller's responsibility that any event sequence is terminated + /// with an EV_SYN/SYN_REPORT/0 event. Otherwise, listeners on the device + /// node will not see the events until the next EV_SYN event is posted. + pub fn write_event(&self, event_type: u32, code: u32, value: i32) -> io::Result<()> { + let result = + unsafe { libevdev::libevdev_uinput_write_event(self.raw(), event_type, code, value) }; + + match result { + 0 => Ok(()), + error => Err(io::Error::from_raw_os_error(-error)), + } + } +} + +impl Drop for UInputDevice { + fn drop(&mut self) { + unsafe { + libevdev::libevdev_uinput_destroy(self.raw()); + } + } +} + +impl std::fmt::Debug for UInputDevice { + fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result { + f.debug_struct("UInputDevice") + .field("devnode", &self.devnode()) + .finish() + } +} diff --git a/evdev/src/main/rust/evdev/src/util.rs b/evdev/src/main/rust/evdev/src/util.rs new file mode 100644 index 0000000000..f74fb7c84c --- /dev/null +++ b/evdev/src/main/rust/evdev/src/util.rs @@ -0,0 +1,435 @@ +use crate::enums::*; +use crate::libevdev as raw; +use libc::{c_char, c_uint}; +use log; +use log::warn; +use std::ffi::{CStr, CString}; +use std::fmt; + +pub(crate) unsafe fn ptr_to_str(ptr: *const c_char) -> Option<&'static str> { + let slice = CStr::from_ptr(ptr.as_ref()?); + let buf = slice.to_bytes(); + std::str::from_utf8(buf).ok() +} + +pub struct EventTypeIterator { + current: EventType, +} + +pub struct EventCodeIterator { + current: EventCode, +} + +pub struct InputPropIterator { + current: InputProp, +} + +impl EventTypeIterator { + pub fn new() -> Self { + EventTypeIterator { + current: EventType::EV_SYN, + } + } +} + +impl EventCodeIterator { + pub fn new(event_type: &EventType) -> Self { + let event_code = match *event_type { + EventType::EV_SYN => EventCode::EV_SYN(EV_SYN::SYN_REPORT), + EventType::EV_KEY => EventCode::EV_KEY(EV_KEY::KEY_RESERVED), + EventType::EV_REL => EventCode::EV_REL(EV_REL::REL_X), + EventType::EV_ABS => EventCode::EV_ABS(EV_ABS::ABS_X), + EventType::EV_MSC => EventCode::EV_MSC(EV_MSC::MSC_SERIAL), + EventType::EV_SW => EventCode::EV_SW(EV_SW::SW_LID), + EventType::EV_LED => EventCode::EV_LED(EV_LED::LED_NUML), + EventType::EV_SND => EventCode::EV_SND(EV_SND::SND_CLICK), + EventType::EV_REP => EventCode::EV_REP(EV_REP::REP_DELAY), + EventType::EV_FF => EventCode::EV_FF(EV_FF::FF_STATUS_STOPPED), + EventType::EV_FF_STATUS => EventCode::EV_FF_STATUS(EV_FF::FF_STATUS_STOPPED), + _ => EventCode::EV_MAX, + }; + + EventCodeIterator { + current: event_code, + } + } +} + +impl InputPropIterator { + pub fn new() -> Self { + InputPropIterator { + current: InputProp::INPUT_PROP_POINTER, + } + } +} + +pub fn event_code_to_int(event_code: &EventCode) -> (c_uint, c_uint) { + match *event_code { + EventCode::EV_SYN(code) => (EventType::EV_SYN as c_uint, code as c_uint), + EventCode::EV_KEY(code) => (EventType::EV_KEY as c_uint, code as c_uint), + EventCode::EV_REL(code) => (EventType::EV_REL as c_uint, code as c_uint), + EventCode::EV_ABS(code) => (EventType::EV_ABS as c_uint, code as c_uint), + EventCode::EV_MSC(code) => (EventType::EV_MSC as c_uint, code as c_uint), + EventCode::EV_SW(code) => (EventType::EV_SW as c_uint, code as c_uint), + EventCode::EV_LED(code) => (EventType::EV_LED as c_uint, code as c_uint), + EventCode::EV_SND(code) => (EventType::EV_SND as c_uint, code as c_uint), + EventCode::EV_REP(code) => (EventType::EV_REP as c_uint, code as c_uint), + EventCode::EV_FF(code) => (EventType::EV_FF as c_uint, code as c_uint), + EventCode::EV_FF_STATUS(code) => (EventType::EV_FF_STATUS as c_uint, code as c_uint), + EventCode::EV_UNK { + event_type, + event_code, + } => (event_type as c_uint, event_code as c_uint), + _ => { + warn!("Event code not found"); + (0, 0) + } + } +} + +pub fn int_to_event_code(event_type: c_uint, event_code: c_uint) -> EventCode { + let ev_type: EventType = int_to_event_type(event_type as u32).unwrap(); + let code = event_code as u32; + + let ev_code = match ev_type { + EventType::EV_SYN => int_to_ev_syn(code).map(EventCode::EV_SYN), + EventType::EV_KEY => int_to_ev_key(code).map(EventCode::EV_KEY), + EventType::EV_ABS => int_to_ev_abs(code).map(EventCode::EV_ABS), + EventType::EV_REL => int_to_ev_rel(code).map(EventCode::EV_REL), + EventType::EV_MSC => int_to_ev_msc(code).map(EventCode::EV_MSC), + EventType::EV_SW => int_to_ev_sw(code).map(EventCode::EV_SW), + EventType::EV_LED => int_to_ev_led(code).map(EventCode::EV_LED), + EventType::EV_SND => int_to_ev_snd(code).map(EventCode::EV_SND), + EventType::EV_REP => int_to_ev_rep(code).map(EventCode::EV_REP), + EventType::EV_FF => int_to_ev_ff(code).map(EventCode::EV_FF), + EventType::EV_PWR => Some(EventCode::EV_PWR), + EventType::EV_FF_STATUS => int_to_ev_ff(code).map(EventCode::EV_FF_STATUS), + EventType::EV_UNK => None, + EventType::EV_MAX => Some(EventCode::EV_MAX), + }; + + ev_code.unwrap_or(EventCode::EV_UNK { + event_type, + event_code, + }) +} + +impl fmt::Display for EventType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_event_type_get_name(*self as c_uint)) }.unwrap_or("") + ) + } +} + +impl fmt::Display for EventCode { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let (ev_type, ev_code) = event_code_to_int(self); + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_event_code_get_name(ev_type, ev_code)) } + .unwrap_or("") + ) + } +} + +impl fmt::Display for InputProp { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!( + f, + "{}", + unsafe { ptr_to_str(raw::libevdev_property_get_name(*self as c_uint)) }.unwrap_or("") + ) + } +} + +impl EventType { + pub fn iter(&self) -> EventTypeIterator { + EventTypeIterator { current: *self } + } + + /// The given type constant for the passed name or Errno if not found. + pub fn from_str(name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = unsafe { raw::libevdev_event_type_from_name(name.as_ptr()) }; + + match result { + -1 => None, + k => int_to_event_type(k as u32), + } + } + + /// The max value defined for the given event type, e.g. ABS_MAX for a type + /// of EV_ABS, or Errno for an invalid type. + pub fn get_max(ev_type: &EventType) -> Option { + let result = unsafe { raw::libevdev_event_type_get_max(*ev_type as c_uint) }; + + match result { + k if k < 0 => None, + k => Some(k as u32), + } + } +} + +impl EventCode { + pub fn iter(&self) -> EventCodeIterator { + EventCodeIterator { current: *self } + } + + /// Look up an event code by its type and name. Event codes start with a fixed + /// prefix followed by their name (eg., "ABS_X"). The prefix must be included in + /// the name. It returns the constant assigned to the event code or Errno if not + /// found. + pub fn from_str(ev_type: &EventType, name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = + unsafe { raw::libevdev_event_code_from_name(*ev_type as c_uint, name.as_ptr()) }; + + match result { + -1 => None, + k => Some(int_to_event_code(*ev_type as u32, k as u32)), + } + } +} + +impl InputProp { + pub fn iter(&self) -> InputPropIterator { + InputPropIterator { current: *self } + } + + /// Look up an input property by its name. Properties start with the fixed + /// prefix "INPUT_PROP_" followed by their name (eg., "INPUT_PROP_POINTER"). + /// The prefix must be included in the name. It returns the constant assigned + /// to the property or Errno if not found. + pub fn from_str(name: &str) -> Option { + let name = CString::new(name).unwrap(); + let result = unsafe { raw::libevdev_property_from_name(name.as_ptr()) }; + + match result { + -1 => None, + k => int_to_input_prop(k as u32), + } + } +} + +// Iterator trait for the enum iterators +impl Iterator for EventTypeIterator { + type Item = EventType; + + fn next(&mut self) -> Option { + match self.current { + EventType::EV_MAX => None, + _ => { + let mut raw_code = (self.current as u32) + 1; + loop { + match int_to_event_type(raw_code) { + // TODO: Find a way to iterate over Unknown types + Some(EventType::EV_UNK) => raw_code += 1, + Some(x) => { + let code = self.current; + self.current = x; + return Some(code); + } + None => raw_code += 1, + } + } + } + } + } +} + +impl Iterator for EventCodeIterator { + type Item = EventCode; + + fn next(&mut self) -> Option { + match self.current { + EventCode::EV_SYN(code) => match code { + EV_SYN::SYN_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_syn(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SYN(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_KEY(code) => match code { + EV_KEY::KEY_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_key(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_KEY(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_REL(code) => match code { + EV_REL::REL_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_rel(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_REL(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_ABS(code) => match code { + EV_ABS::ABS_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_abs(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_ABS(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_MSC(code) => match code { + EV_MSC::MSC_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_msc(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_MSC(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_SW(code) => match code { + EV_SW::SW_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_sw(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SW(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_LED(code) => match code { + EV_LED::LED_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_led(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_LED(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_SND(code) => match code { + EV_SND::SND_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_snd(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_SND(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_REP(code) => match code { + EV_REP::REP_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_rep(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_REP(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + EventCode::EV_FF(code) => match code { + EV_FF::FF_MAX => None, + _ => { + let mut raw_code = (code as u32) + 1; + loop { + match int_to_ev_ff(raw_code) { + Some(x) => { + let ev_code = self.current; + self.current = EventCode::EV_FF(x); + return Some(ev_code); + } + None => raw_code += 1, + } + } + } + }, + _ => None, + } + } +} + +impl Iterator for InputPropIterator { + type Item = InputProp; + + fn next(&mut self) -> Option { + match self.current { + InputProp::INPUT_PROP_MAX => None, + _ => { + let mut raw_enum = (self.current as u32) + 1; + loop { + match int_to_input_prop(raw_enum) { + Some(x) => { + let prop = self.current; + self.current = x; + return Some(prop); + } + None => raw_enum += 1, + } + } + } + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/.cargo/config.toml b/evdev/src/main/rust/evdev_manager/.cargo/config.toml new file mode 100644 index 0000000000..f2084c6b7f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/.cargo/config.toml @@ -0,0 +1,15 @@ +[target.armv7-linux-androideabi] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.aarch64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.i686-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + +[target.x86_64-linux-android] +rustflags = ["-C", "link-arg=-Wl,-z,max-page-size=16384"] + + + + diff --git a/evdev/src/main/rust/evdev_manager/Cargo.lock b/evdev/src/main/rust/evdev_manager/Cargo.lock new file mode 100644 index 0000000000..efe3500145 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.lock @@ -0,0 +1,558 @@ +# This file is automatically @generated by Cargo. +# It is not intended for manual editing. +version = 4 + +[[package]] +name = "assertables" +version = "9.8.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "59051ec02907378a67b0ba1b8631121f5388c8dbbb3cec8c749d8f93c2c3c211" + +[[package]] +name = "bimap" +version = "0.6.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "230c5f1ca6a325a32553f8640d31ac9b49f2411e901e427570154868b46da4f7" + +[[package]] +name = "bitflags" +version = "1.3.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" + +[[package]] +name = "bitflags" +version = "2.10.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "812e12b5285cc515a9c72a5c1d3b6d46a19dac5acfef5265968c166106e31dd3" + +[[package]] +name = "bytes" +version = "1.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" + +[[package]] +name = "cc" +version = "1.2.49" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] + +[[package]] +name = "cesu8" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6d43a04d8753f35258c91f8ec639f792891f748a1edbd759cf1dcea3382ad83c" + +[[package]] +name = "cfg-if" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "combine" +version = "4.6.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ba5a308b75df32fe02788e748662718f03fde005016435c444eea572398219fd" +dependencies = [ + "bytes", + "memchr", +] + +[[package]] +name = "diff" +version = "0.1.13" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "56254986775e3233ffa9c4d7d3faaf6d36a2c09d30b20687e9f88bc8bafc16c8" + +[[package]] +name = "evdev" +version = "0.1.0" +dependencies = [ + "bitflags 2.10.0", + "cc", + "libc 1.0.0-alpha.1", + "log", +] + +[[package]] +name = "evdev_manager_core" +version = "0.1.0" +dependencies = [ + "assertables", + "bimap", + "evdev", + "glob", + "libc 0.2.177", + "log", + "mio", + "notify", + "pretty_assertions", + "slab", + "tokio", +] + +[[package]] +name = "evdev_manager_jni" +version = "0.1.0" +dependencies = [ + "evdev", + "evdev_manager_core", + "jni", + "libc 0.2.177", + "log", +] + +[[package]] +name = "find-msvc-tools" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" + +[[package]] +name = "fsevent-sys" +version = "4.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "76ee7a02da4d231650c7cea31349b889be2f45ddb3ef3032d2ec8185f6313fd2" +dependencies = [ + "libc 0.2.177", +] + +[[package]] +name = "glob" +version = "0.3.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0cc23270f6e1808e30a928bdc84dea0b9b4136a8bc82338574f23baf47bbd280" + +[[package]] +name = "inotify" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f37dccff2791ab604f9babef0ba14fbe0be30bd368dc541e2b08d07c8aa908f3" +dependencies = [ + "bitflags 2.10.0", + "inotify-sys", + "libc 0.2.177", +] + +[[package]] +name = "inotify-sys" +version = "0.1.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e05c02b5e89bff3b946cedeca278abc628fe811e604f027c45a8aa3cf793d0eb" +dependencies = [ + "libc 0.2.177", +] + +[[package]] +name = "jni" +version = "0.21.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1a87aa2bb7d2af34197c04845522473242e1aa17c12f4935d5856491a7fb8c97" +dependencies = [ + "cesu8", + "cfg-if", + "combine", + "jni-sys", + "log", + "thiserror", + "walkdir", + "windows-sys 0.45.0", +] + +[[package]] +name = "jni-sys" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8eaf4bc02d17cbdd7ff4c7438cafcdf7fb9a4613313ad11b4f8fefe7d3fa0130" + +[[package]] +name = "kqueue" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eac30106d7dce88daf4a3fcb4879ea939476d5074a9b7ddd0fb97fa4bed5596a" +dependencies = [ + "kqueue-sys", + "libc 0.2.177", +] + +[[package]] +name = "kqueue-sys" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ed9625ffda8729b85e45cf04090035ac368927b8cebc34898e7c120f52e4838b" +dependencies = [ + "bitflags 1.3.2", + "libc 0.2.177", +] + +[[package]] +name = "libc" +version = "0.2.177" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "2874a2af47a2325c2001a6e6fad9b16a53b802102b528163885171cf92b15976" + +[[package]] +name = "libc" +version = "1.0.0-alpha.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7222002e5385b4d9327755661e3847c970e8fbf9dea6da8c57f16e8cfbff53a8" + +[[package]] +name = "log" +version = "0.4.28" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "34080505efa8e45a4b816c349525ebe327ceaa8559756f0356cba97ef3bf7432" + +[[package]] +name = "main" +version = "0.1.0" +dependencies = [ + "evdev_manager_jni", +] + +[[package]] +name = "memchr" +version = "2.7.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273" + +[[package]] +name = "mio" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69d83b0086dc8ecf3ce9ae2874b2d1290252e2a30720bea58a5c6639b0092873" +dependencies = [ + "libc 0.2.177", + "log", + "wasi", + "windows-sys 0.61.2", +] + +[[package]] +name = "notify" +version = "8.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4d3d07927151ff8575b7087f245456e549fea62edf0ec4e565a5ee50c8402bc3" +dependencies = [ + "bitflags 2.10.0", + "fsevent-sys", + "inotify", + "kqueue", + "libc 0.2.177", + "log", + "mio", + "notify-types", + "walkdir", + "windows-sys 0.60.2", +] + +[[package]] +name = "notify-types" +version = "2.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e0826a989adedc2a244799e823aece04662b66609d96af8dff7ac6df9a8925d" + +[[package]] +name = "pin-project-lite" +version = "0.2.16" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3b3cff922bd51709b605d9ead9aa71031d81447142d828eb4a6eba76fe619f9b" + +[[package]] +name = "pretty_assertions" +version = "1.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3ae130e2f271fbc2ac3a40fb1d07180839cdbbe443c7a27e1e3c13c5cac0116d" +dependencies = [ + "diff", + "yansi", +] + +[[package]] +name = "proc-macro2" +version = "1.0.103" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" +dependencies = [ + "unicode-ident", +] + +[[package]] +name = "quote" +version = "1.0.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" +dependencies = [ + "proc-macro2", +] + +[[package]] +name = "same-file" +version = "1.0.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "93fc1dc3aaa9bfed95e02e6eadabb4baf7e3078b0bd1b4d7b6b0b68378900502" +dependencies = [ + "winapi-util", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" + +[[package]] +name = "slab" +version = "0.4.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7a2ae44ef20feb57a68b23d846850f861394c2e02dc425a50098ae8c90267589" + +[[package]] +name = "syn" +version = "2.0.110" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a99801b5bd34ede4cf3fc688c5919368fea4e4814a4664359503e6015b280aea" +dependencies = [ + "proc-macro2", + "quote", + "unicode-ident", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "tokio" +version = "1.48.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ff360e02eab121e0bc37a2d3b4d4dc622e6eda3a8e5253d5435ecf5bd4c68408" +dependencies = [ + "pin-project-lite", + "tokio-macros", +] + +[[package]] +name = "tokio-macros" +version = "2.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "unicode-ident" +version = "1.0.22" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" + +[[package]] +name = "walkdir" +version = "2.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "29790946404f91d9c5d06f9874efddea1dc06c5efe94541a7d6863108e3a5e4b" +dependencies = [ + "same-file", + "winapi-util", +] + +[[package]] +name = "wasi" +version = "0.11.1+wasi-snapshot-preview1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" + +[[package]] +name = "winapi-util" +version = "0.1.11" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c2a7b1c03c876122aa43f3020e6c3c3ee5c05081c9a00739faf7503aeba10d22" +dependencies = [ + "windows-sys 0.61.2", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-sys" +version = "0.45.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "75283be5efb2831d37ea142365f009c02ec203cd29a3ebecbc093d52315b66d0" +dependencies = [ + "windows-targets 0.42.2", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-targets" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e5180c00cd44c9b1c88adb3693291f1cd93605ded80c250a75d472756b4d071" +dependencies = [ + "windows_aarch64_gnullvm 0.42.2", + "windows_aarch64_msvc 0.42.2", + "windows_i686_gnu 0.42.2", + "windows_i686_msvc 0.42.2", + "windows_x86_64_gnu 0.42.2", + "windows_x86_64_gnullvm 0.42.2", + "windows_x86_64_msvc 0.42.2", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" +dependencies = [ + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", +] + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" + +[[package]] +name = "windows_i686_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" + +[[package]] +name = "windows_i686_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.42.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" + +[[package]] +name = "yansi" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cfe53a6657fd280eaa890a3bc59152892ffa3e30101319d168b781ed6529b049" diff --git a/evdev/src/main/rust/evdev_manager/Cargo.toml b/evdev/src/main/rust/evdev_manager/Cargo.toml new file mode 100644 index 0000000000..6b9edfb09f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/Cargo.toml @@ -0,0 +1,7 @@ +[workspace] +members = [ + "main", + "core", + "jni", +] +resolver = "3" diff --git a/evdev/src/main/rust/evdev_manager/core/Cargo.toml b/evdev/src/main/rust/evdev_manager/core/Cargo.toml new file mode 100644 index 0000000000..854d5b5bae --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/Cargo.toml @@ -0,0 +1,23 @@ +[package] +name = "evdev_manager_core" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager_core" +crate-type = ["rlib"] + +[dependencies] +log = "0.4.28" +evdev = { path = "../../evdev" } +libc = "0.2.177" +mio = { version = "1.1.0", features = ["os-poll", "os-ext"] } +slab = "0.4.11" +tokio = { version = "1.48.0", features = ["rt", "macros", "rt-multi-thread"] } +bimap = "0.6.3" +notify = "8.2.0" + +[dev-dependencies] +glob = "0.3" +pretty_assertions = "1.4.1" +assertables = "9.8.2" diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs b/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs new file mode 100644 index 0000000000..c88417091d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/android_codes.rs @@ -0,0 +1,659 @@ +use std::os::raw::c_uint; + +pub const AKEYCODE_UNKNOWN: c_uint = 0; +pub const AKEYCODE_SOFT_LEFT: c_uint = 1; +pub const AKEYCODE_SOFT_RIGHT: c_uint = 2; +pub const AKEYCODE_HOME: c_uint = 3; +pub const AKEYCODE_BACK: c_uint = 4; +pub const AKEYCODE_CALL: c_uint = 5; +pub const AKEYCODE_ENDCALL: c_uint = 6; +pub const AKEYCODE_0: c_uint = 7; +pub const AKEYCODE_1: c_uint = 8; +pub const AKEYCODE_2: c_uint = 9; +pub const AKEYCODE_3: c_uint = 10; +pub const AKEYCODE_4: c_uint = 11; +pub const AKEYCODE_5: c_uint = 12; +pub const AKEYCODE_6: c_uint = 13; +pub const AKEYCODE_7: c_uint = 14; +pub const AKEYCODE_8: c_uint = 15; +pub const AKEYCODE_9: c_uint = 16; +pub const AKEYCODE_STAR: c_uint = 17; +pub const AKEYCODE_POUND: c_uint = 18; +pub const AKEYCODE_DPAD_UP: c_uint = 19; +pub const AKEYCODE_DPAD_DOWN: c_uint = 20; +pub const AKEYCODE_DPAD_LEFT: c_uint = 21; +pub const AKEYCODE_DPAD_RIGHT: c_uint = 22; +pub const AKEYCODE_DPAD_CENTER: c_uint = 23; +pub const AKEYCODE_VOLUME_UP: c_uint = 24; +pub const AKEYCODE_VOLUME_DOWN: c_uint = 25; +pub const AKEYCODE_POWER: c_uint = 26; +pub const AKEYCODE_CAMERA: c_uint = 27; +pub const AKEYCODE_CLEAR: c_uint = 28; +pub const AKEYCODE_A: c_uint = 29; +pub const AKEYCODE_B: c_uint = 30; +pub const AKEYCODE_C: c_uint = 31; +pub const AKEYCODE_D: c_uint = 32; +pub const AKEYCODE_E: c_uint = 33; +pub const AKEYCODE_F: c_uint = 34; +pub const AKEYCODE_G: c_uint = 35; +pub const AKEYCODE_H: c_uint = 36; +pub const AKEYCODE_I: c_uint = 37; +pub const AKEYCODE_J: c_uint = 38; +pub const AKEYCODE_K: c_uint = 39; +pub const AKEYCODE_L: c_uint = 40; +pub const AKEYCODE_M: c_uint = 41; +pub const AKEYCODE_N: c_uint = 42; +pub const AKEYCODE_O: c_uint = 43; +pub const AKEYCODE_P: c_uint = 44; +pub const AKEYCODE_Q: c_uint = 45; +pub const AKEYCODE_R: c_uint = 46; +pub const AKEYCODE_S: c_uint = 47; +pub const AKEYCODE_T: c_uint = 48; +pub const AKEYCODE_U: c_uint = 49; +pub const AKEYCODE_V: c_uint = 50; +pub const AKEYCODE_W: c_uint = 51; +pub const AKEYCODE_X: c_uint = 52; +pub const AKEYCODE_Y: c_uint = 53; +pub const AKEYCODE_Z: c_uint = 54; +pub const AKEYCODE_COMMA: c_uint = 55; +pub const AKEYCODE_PERIOD: c_uint = 56; +pub const AKEYCODE_ALT_LEFT: c_uint = 57; +pub const AKEYCODE_ALT_RIGHT: c_uint = 58; +pub const AKEYCODE_SHIFT_LEFT: c_uint = 59; +pub const AKEYCODE_SHIFT_RIGHT: c_uint = 60; +pub const AKEYCODE_TAB: c_uint = 61; +pub const AKEYCODE_SPACE: c_uint = 62; +pub const AKEYCODE_SYM: c_uint = 63; +pub const AKEYCODE_EXPLORER: c_uint = 64; +pub const AKEYCODE_ENVELOPE: c_uint = 65; +pub const AKEYCODE_ENTER: c_uint = 66; +pub const AKEYCODE_DEL: c_uint = 67; +pub const AKEYCODE_GRAVE: c_uint = 68; +pub const AKEYCODE_MINUS: c_uint = 69; +pub const AKEYCODE_EQUALS: c_uint = 70; +pub const AKEYCODE_LEFT_BRACKET: c_uint = 71; +pub const AKEYCODE_RIGHT_BRACKET: c_uint = 72; +pub const AKEYCODE_BACKSLASH: c_uint = 73; +pub const AKEYCODE_SEMICOLON: c_uint = 74; +pub const AKEYCODE_APOSTROPHE: c_uint = 75; +pub const AKEYCODE_SLASH: c_uint = 76; +pub const AKEYCODE_AT: c_uint = 77; +pub const AKEYCODE_NUM: c_uint = 78; +pub const AKEYCODE_HEADSETHOOK: c_uint = 79; +pub const AKEYCODE_FOCUS: c_uint = 80; +pub const AKEYCODE_PLUS: c_uint = 81; +pub const AKEYCODE_MENU: c_uint = 82; +pub const AKEYCODE_NOTIFICATION: c_uint = 83; +pub const AKEYCODE_SEARCH: c_uint = 84; +pub const AKEYCODE_MEDIA_PLAY_PAUSE: c_uint = 85; +pub const AKEYCODE_MEDIA_STOP: c_uint = 86; +pub const AKEYCODE_MEDIA_NEXT: c_uint = 87; +pub const AKEYCODE_MEDIA_PREVIOUS: c_uint = 88; +pub const AKEYCODE_MEDIA_REWIND: c_uint = 89; +pub const AKEYCODE_MEDIA_FAST_FORWARD: c_uint = 90; +pub const AKEYCODE_MUTE: c_uint = 91; +pub const AKEYCODE_PAGE_UP: c_uint = 92; +pub const AKEYCODE_PAGE_DOWN: c_uint = 93; +pub const AKEYCODE_PICTSYMBOLS: c_uint = 94; +pub const AKEYCODE_SWITCH_CHARSET: c_uint = 95; +pub const AKEYCODE_BUTTON_A: c_uint = 96; +pub const AKEYCODE_BUTTON_B: c_uint = 97; +pub const AKEYCODE_BUTTON_C: c_uint = 98; +pub const AKEYCODE_BUTTON_X: c_uint = 99; +pub const AKEYCODE_BUTTON_Y: c_uint = 100; +pub const AKEYCODE_BUTTON_Z: c_uint = 101; +pub const AKEYCODE_BUTTON_L1: c_uint = 102; +pub const AKEYCODE_BUTTON_R1: c_uint = 103; +pub const AKEYCODE_BUTTON_L2: c_uint = 104; +pub const AKEYCODE_BUTTON_R2: c_uint = 105; +pub const AKEYCODE_BUTTON_THUMBL: c_uint = 106; +pub const AKEYCODE_BUTTON_THUMBR: c_uint = 107; +pub const AKEYCODE_BUTTON_START: c_uint = 108; +pub const AKEYCODE_BUTTON_SELECT: c_uint = 109; +pub const AKEYCODE_BUTTON_MODE: c_uint = 110; +pub const AKEYCODE_ESCAPE: c_uint = 111; +pub const AKEYCODE_FORWARD_DEL: c_uint = 112; +pub const AKEYCODE_CTRL_LEFT: c_uint = 113; +pub const AKEYCODE_CTRL_RIGHT: c_uint = 114; +pub const AKEYCODE_CAPS_LOCK: c_uint = 115; +pub const AKEYCODE_SCROLL_LOCK: c_uint = 116; +pub const AKEYCODE_META_LEFT: c_uint = 117; +pub const AKEYCODE_META_RIGHT: c_uint = 118; +pub const AKEYCODE_FUNCTION: c_uint = 119; +pub const AKEYCODE_SYSRQ: c_uint = 120; +pub const AKEYCODE_BREAK: c_uint = 121; +pub const AKEYCODE_MOVE_HOME: c_uint = 122; +pub const AKEYCODE_MOVE_END: c_uint = 123; +pub const AKEYCODE_INSERT: c_uint = 124; +pub const AKEYCODE_FORWARD: c_uint = 125; +pub const AKEYCODE_MEDIA_PLAY: c_uint = 126; +pub const AKEYCODE_MEDIA_PAUSE: c_uint = 127; +pub const AKEYCODE_MEDIA_CLOSE: c_uint = 128; +pub const AKEYCODE_MEDIA_EJECT: c_uint = 129; +pub const AKEYCODE_MEDIA_RECORD: c_uint = 130; +pub const AKEYCODE_F1: c_uint = 131; +pub const AKEYCODE_F2: c_uint = 132; +pub const AKEYCODE_F3: c_uint = 133; +pub const AKEYCODE_F4: c_uint = 134; +pub const AKEYCODE_F5: c_uint = 135; +pub const AKEYCODE_F6: c_uint = 136; +pub const AKEYCODE_F7: c_uint = 137; +pub const AKEYCODE_F8: c_uint = 138; +pub const AKEYCODE_F9: c_uint = 139; +pub const AKEYCODE_F10: c_uint = 140; +pub const AKEYCODE_F11: c_uint = 141; +pub const AKEYCODE_F12: c_uint = 142; +pub const AKEYCODE_NUM_LOCK: c_uint = 143; +pub const AKEYCODE_NUMPAD_0: c_uint = 144; +pub const AKEYCODE_NUMPAD_1: c_uint = 145; +pub const AKEYCODE_NUMPAD_2: c_uint = 146; +pub const AKEYCODE_NUMPAD_3: c_uint = 147; +pub const AKEYCODE_NUMPAD_4: c_uint = 148; +pub const AKEYCODE_NUMPAD_5: c_uint = 149; +pub const AKEYCODE_NUMPAD_6: c_uint = 150; +pub const AKEYCODE_NUMPAD_7: c_uint = 151; +pub const AKEYCODE_NUMPAD_8: c_uint = 152; +pub const AKEYCODE_NUMPAD_9: c_uint = 153; +pub const AKEYCODE_NUMPAD_DIVIDE: c_uint = 154; +pub const AKEYCODE_NUMPAD_MULTIPLY: c_uint = 155; +pub const AKEYCODE_NUMPAD_SUBTRACT: c_uint = 156; +pub const AKEYCODE_NUMPAD_ADD: c_uint = 157; +pub const AKEYCODE_NUMPAD_DOT: c_uint = 158; +pub const AKEYCODE_NUMPAD_COMMA: c_uint = 159; +pub const AKEYCODE_NUMPAD_ENTER: c_uint = 160; +pub const AKEYCODE_NUMPAD_EQUALS: c_uint = 161; +pub const AKEYCODE_NUMPAD_LEFT_PAREN: c_uint = 162; +pub const AKEYCODE_NUMPAD_RIGHT_PAREN: c_uint = 163; +pub const AKEYCODE_VOLUME_MUTE: c_uint = 164; +pub const AKEYCODE_INFO: c_uint = 165; +pub const AKEYCODE_CHANNEL_UP: c_uint = 166; +pub const AKEYCODE_CHANNEL_DOWN: c_uint = 167; +pub const AKEYCODE_ZOOM_IN: c_uint = 168; +pub const AKEYCODE_ZOOM_OUT: c_uint = 169; +pub const AKEYCODE_TV: c_uint = 170; +pub const AKEYCODE_WINDOW: c_uint = 171; +pub const AKEYCODE_GUIDE: c_uint = 172; +pub const AKEYCODE_DVR: c_uint = 173; +pub const AKEYCODE_BOOKMARK: c_uint = 174; +pub const AKEYCODE_CAPTIONS: c_uint = 175; +pub const AKEYCODE_SETTINGS: c_uint = 176; +pub const AKEYCODE_TV_POWER: c_uint = 177; +pub const AKEYCODE_TV_INPUT: c_uint = 178; +pub const AKEYCODE_STB_POWER: c_uint = 179; +pub const AKEYCODE_STB_INPUT: c_uint = 180; +pub const AKEYCODE_AVR_POWER: c_uint = 181; +pub const AKEYCODE_AVR_INPUT: c_uint = 182; +pub const AKEYCODE_PROG_RED: c_uint = 183; +pub const AKEYCODE_PROG_GREEN: c_uint = 184; +pub const AKEYCODE_PROG_YELLOW: c_uint = 185; +pub const AKEYCODE_PROG_BLUE: c_uint = 186; +pub const AKEYCODE_APP_SWITCH: c_uint = 187; +pub const AKEYCODE_BUTTON_1: c_uint = 188; +pub const AKEYCODE_BUTTON_2: c_uint = 189; +pub const AKEYCODE_BUTTON_3: c_uint = 190; +pub const AKEYCODE_BUTTON_4: c_uint = 191; +pub const AKEYCODE_BUTTON_5: c_uint = 192; +pub const AKEYCODE_BUTTON_6: c_uint = 193; +pub const AKEYCODE_BUTTON_7: c_uint = 194; +pub const AKEYCODE_BUTTON_8: c_uint = 195; +pub const AKEYCODE_BUTTON_9: c_uint = 196; +pub const AKEYCODE_BUTTON_10: c_uint = 197; +pub const AKEYCODE_BUTTON_11: c_uint = 198; +pub const AKEYCODE_BUTTON_12: c_uint = 199; +pub const AKEYCODE_BUTTON_13: c_uint = 200; +pub const AKEYCODE_BUTTON_14: c_uint = 201; +pub const AKEYCODE_BUTTON_15: c_uint = 202; +pub const AKEYCODE_BUTTON_16: c_uint = 203; +pub const AKEYCODE_LANGUAGE_SWITCH: c_uint = 204; +pub const AKEYCODE_MANNER_MODE: c_uint = 205; +pub const AKEYCODE_3D_MODE: c_uint = 206; +pub const AKEYCODE_CONTACTS: c_uint = 207; +pub const AKEYCODE_CALENDAR: c_uint = 208; +pub const AKEYCODE_MUSIC: c_uint = 209; +pub const AKEYCODE_CALCULATOR: c_uint = 210; +pub const AKEYCODE_ZENKAKU_HANKAKU: c_uint = 211; +pub const AKEYCODE_EISU: c_uint = 212; +pub const AKEYCODE_MUHENKAN: c_uint = 213; +pub const AKEYCODE_HENKAN: c_uint = 214; +pub const AKEYCODE_KATAKANA_HIRAGANA: c_uint = 215; +pub const AKEYCODE_YEN: c_uint = 216; +pub const AKEYCODE_RO: c_uint = 217; +pub const AKEYCODE_KANA: c_uint = 218; +pub const AKEYCODE_ASSIST: c_uint = 219; +pub const AKEYCODE_BRIGHTNESS_DOWN: c_uint = 220; +pub const AKEYCODE_BRIGHTNESS_UP: c_uint = 221; +pub const AKEYCODE_MEDIA_AUDIO_TRACK: c_uint = 222; +pub const AKEYCODE_SLEEP: c_uint = 223; +pub const AKEYCODE_WAKEUP: c_uint = 224; +pub const AKEYCODE_PAIRING: c_uint = 225; +pub const AKEYCODE_MEDIA_TOP_MENU: c_uint = 226; +pub const AKEYCODE_11: c_uint = 227; +pub const AKEYCODE_12: c_uint = 228; +pub const AKEYCODE_LAST_CHANNEL: c_uint = 229; +pub const AKEYCODE_TV_DATA_SERVICE: c_uint = 230; +pub const AKEYCODE_VOICE_ASSIST: c_uint = 231; +pub const AKEYCODE_TV_RADIO_SERVICE: c_uint = 232; +pub const AKEYCODE_TV_TELETEXT: c_uint = 233; +pub const AKEYCODE_TV_NUMBER_ENTRY: c_uint = 234; +pub const AKEYCODE_TV_TERRESTRIAL_ANALOG: c_uint = 235; +pub const AKEYCODE_TV_TERRESTRIAL_DIGITAL: c_uint = 236; +pub const AKEYCODE_TV_SATELLITE: c_uint = 237; +pub const AKEYCODE_TV_SATELLITE_BS: c_uint = 238; +pub const AKEYCODE_TV_SATELLITE_CS: c_uint = 239; +pub const AKEYCODE_TV_SATELLITE_SERVICE: c_uint = 240; +pub const AKEYCODE_TV_NETWORK: c_uint = 241; +pub const AKEYCODE_TV_ANTENNA_CABLE: c_uint = 242; +pub const AKEYCODE_TV_INPUT_HDMI_1: c_uint = 243; +pub const AKEYCODE_TV_INPUT_HDMI_2: c_uint = 244; +pub const AKEYCODE_TV_INPUT_HDMI_3: c_uint = 245; +pub const AKEYCODE_TV_INPUT_HDMI_4: c_uint = 246; +pub const AKEYCODE_TV_INPUT_COMPOSITE_1: c_uint = 247; +pub const AKEYCODE_TV_INPUT_COMPOSITE_2: c_uint = 248; +pub const AKEYCODE_TV_INPUT_COMPONENT_1: c_uint = 249; +pub const AKEYCODE_TV_INPUT_COMPONENT_2: c_uint = 250; +pub const AKEYCODE_TV_INPUT_VGA_1: c_uint = 251; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION: c_uint = 252; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP: c_uint = 253; +pub const AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN: c_uint = 254; +pub const AKEYCODE_TV_ZOOM_MODE: c_uint = 255; +pub const AKEYCODE_TV_CONTENTS_MENU: c_uint = 256; +pub const AKEYCODE_TV_MEDIA_CONTEXT_MENU: c_uint = 257; +pub const AKEYCODE_TV_TIMER_PROGRAMMING: c_uint = 258; +pub const AKEYCODE_HELP: c_uint = 259; +pub const AKEYCODE_NAVIGATE_PREVIOUS: c_uint = 260; +pub const AKEYCODE_NAVIGATE_NEXT: c_uint = 261; +pub const AKEYCODE_NAVIGATE_IN: c_uint = 262; +pub const AKEYCODE_NAVIGATE_OUT: c_uint = 263; +pub const AKEYCODE_STEM_PRIMARY: c_uint = 264; +pub const AKEYCODE_STEM_1: c_uint = 265; +pub const AKEYCODE_STEM_2: c_uint = 266; +pub const AKEYCODE_STEM_3: c_uint = 267; +pub const AKEYCODE_DPAD_UP_LEFT: c_uint = 268; +pub const AKEYCODE_DPAD_DOWN_LEFT: c_uint = 269; +pub const AKEYCODE_DPAD_UP_RIGHT: c_uint = 270; +pub const AKEYCODE_DPAD_DOWN_RIGHT: c_uint = 271; +pub const AKEYCODE_MEDIA_SKIP_FORWARD: c_uint = 272; +pub const AKEYCODE_MEDIA_SKIP_BACKWARD: c_uint = 273; +pub const AKEYCODE_MEDIA_STEP_FORWARD: c_uint = 274; +pub const AKEYCODE_MEDIA_STEP_BACKWARD: c_uint = 275; +pub const AKEYCODE_SOFT_SLEEP: c_uint = 276; +pub const AKEYCODE_CUT: c_uint = 277; +pub const AKEYCODE_COPY: c_uint = 278; +pub const AKEYCODE_PASTE: c_uint = 279; +pub const AKEYCODE_SYSTEM_NAVIGATION_UP: c_uint = 280; +pub const AKEYCODE_SYSTEM_NAVIGATION_DOWN: c_uint = 281; +pub const AKEYCODE_SYSTEM_NAVIGATION_LEFT: c_uint = 282; +pub const AKEYCODE_SYSTEM_NAVIGATION_RIGHT: c_uint = 283; +pub const AKEYCODE_ALL_APPS: c_uint = 284; +pub const AKEYCODE_REFRESH: c_uint = 285; +pub const AKEYCODE_THUMBS_UP: c_uint = 286; +pub const AKEYCODE_THUMBS_DOWN: c_uint = 287; +pub const AKEYCODE_PROFILE_SWITCH: c_uint = 288; +pub const AKEYCODE_VIDEO_APP_1: c_uint = 289; +pub const AKEYCODE_VIDEO_APP_2: c_uint = 290; +pub const AKEYCODE_VIDEO_APP_3: c_uint = 291; +pub const AKEYCODE_VIDEO_APP_4: c_uint = 292; +pub const AKEYCODE_VIDEO_APP_5: c_uint = 293; +pub const AKEYCODE_VIDEO_APP_6: c_uint = 294; +pub const AKEYCODE_VIDEO_APP_7: c_uint = 295; +pub const AKEYCODE_VIDEO_APP_8: c_uint = 296; +pub const AKEYCODE_FEATURED_APP_1: c_uint = 297; +pub const AKEYCODE_FEATURED_APP_2: c_uint = 298; +pub const AKEYCODE_FEATURED_APP_3: c_uint = 299; +pub const AKEYCODE_FEATURED_APP_4: c_uint = 300; +pub const AKEYCODE_DEMO_APP_1: c_uint = 301; +pub const AKEYCODE_DEMO_APP_2: c_uint = 302; +pub const AKEYCODE_DEMO_APP_3: c_uint = 303; +pub const AKEYCODE_DEMO_APP_4: c_uint = 304; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_DOWN: c_uint = 305; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_UP: c_uint = 306; +pub const AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE: c_uint = 307; +pub const AKEYCODE_STYLUS_BUTTON_PRIMARY: c_uint = 308; +pub const AKEYCODE_STYLUS_BUTTON_SECONDARY: c_uint = 309; +pub const AKEYCODE_STYLUS_BUTTON_TERTIARY: c_uint = 310; +pub const AKEYCODE_STYLUS_BUTTON_TAIL: c_uint = 311; +pub const AKEYCODE_RECENT_APPS: c_uint = 312; +pub const AKEYCODE_MACRO_1: c_uint = 313; +pub const AKEYCODE_MACRO_2: c_uint = 314; +pub const AKEYCODE_MACRO_3: c_uint = 315; +pub const AKEYCODE_MACRO_4: c_uint = 316; + +pub const AMOTION_EVENT_AXIS_X: c_uint = 0; +/** Axis constant: Y axis of a motion event. + +- For a touch screen, reports the absolute Y screen position of the center of +the touch contact area. The units are display pixels. +- For a touch pad, reports the absolute Y surface position of the center of the touch +contact area. The units are device-dependent. +- For a mouse, reports the absolute Y screen position of the mouse pointer. +The units are display pixels. +- For a trackball, reports the relative vertical displacement of the trackball. +The value is normalized to a range from -1.0 (up) to 1.0 (down). +- For a joystick, reports the absolute Y position of the joystick. +The value is normalized to a range from -1.0 (up or far) to 1.0 (down or near).*/ +pub const AMOTION_EVENT_AXIS_Y: c_uint = 1; +/** Axis constant: Pressure axis of a motion event. + +- For a touch screen or touch pad, reports the approximate pressure applied to the surface +by a finger or other tool. The value is normalized to a range from +0 (no pressure at all) to 1 (normal pressure), although values higher than 1 +may be generated depending on the calibration of the input device. +- For a trackball, the value is set to 1 if the trackball button is pressed +or 0 otherwise. +- For a mouse, the value is set to 1 if the primary mouse button is pressed +or 0 otherwise.*/ +pub const AMOTION_EVENT_AXIS_PRESSURE: c_uint = 2; +/** Axis constant: Size axis of a motion event. + +- For a touch screen or touch pad, reports the approximate size of the contact area in +relation to the maximum detectable size for the device. The value is normalized +to a range from 0 (smallest detectable size) to 1 (largest detectable size), +although it is not a linear scale. This value is of limited use. +To obtain calibrated size information, see +{@link AMOTION_EVENT_AXIS_TOUCH_MAJOR} or {@link AMOTION_EVENT_AXIS_TOOL_MAJOR}.*/ +pub const AMOTION_EVENT_AXIS_SIZE: c_uint = 3; +/** Axis constant: TouchMajor axis of a motion event. + +- For a touch screen, reports the length of the major axis of an ellipse that +represents the touch area at the point of contact. +The units are display pixels. +- For a touch pad, reports the length of the major axis of an ellipse that +represents the touch area at the point of contact. +The units are device-dependent.*/ +pub const AMOTION_EVENT_AXIS_TOUCH_MAJOR: c_uint = 4; +/** Axis constant: TouchMinor axis of a motion event. + +- For a touch screen, reports the length of the minor axis of an ellipse that +represents the touch area at the point of contact. +The units are display pixels. +- For a touch pad, reports the length of the minor axis of an ellipse that +represents the touch area at the point of contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another.*/ +pub const AMOTION_EVENT_AXIS_TOUCH_MINOR: c_uint = 5; +/** Axis constant: ToolMajor axis of a motion event. + +- For a touch screen, reports the length of the major axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +- For a touch pad, reports the length of the major axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another. + +The tool size may be larger than the touch size since the tool may not be fully +in contact with the touch sensor.*/ +pub const AMOTION_EVENT_AXIS_TOOL_MAJOR: c_uint = 6; +/** Axis constant: ToolMinor axis of a motion event. + +- For a touch screen, reports the length of the minor axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +- For a touch pad, reports the length of the minor axis of an ellipse that +represents the size of the approaching finger or tool used to make contact. +The units are device-dependent. + +When the touch is circular, the major and minor axis lengths will be equal to one another. + +The tool size may be larger than the touch size since the tool may not be fully +in contact with the touch sensor.*/ +pub const AMOTION_EVENT_AXIS_TOOL_MINOR: c_uint = 7; +/** Axis constant: Orientation axis of a motion event. + +- For a touch screen or touch pad, reports the orientation of the finger +or tool in radians relative to the vertical plane of the device. +An angle of 0 radians indicates that the major axis of contact is oriented +upwards, is perfectly circular or is of unknown orientation. A positive angle +indicates that the major axis of contact is oriented to the right. A negative angle +indicates that the major axis of contact is oriented to the left. +The full range is from -PI/2 radians (finger pointing fully left) to PI/2 radians +(finger pointing fully right). +- For a stylus, the orientation indicates the direction in which the stylus +is pointing in relation to the vertical axis of the current orientation of the screen. +The range is from -PI radians to PI radians, where 0 is pointing up, +-PI/2 radians is pointing left, -PI or PI radians is pointing down, and PI/2 radians +is pointing right. See also #AMOTION_EVENT_AXIS_TILT.*/ +pub const AMOTION_EVENT_AXIS_ORIENTATION: c_uint = 8; +/** Axis constant: Vertical Scroll axis of a motion event. + +- For a mouse, reports the relative movement of the vertical scroll wheel. +The value is normalized to a range from -1.0 (down) to 1.0 (up). + +This axis should be used to scroll views vertically.*/ +pub const AMOTION_EVENT_AXIS_VSCROLL: c_uint = 9; +/** Axis constant: Horizontal Scroll axis of a motion event. + +- For a mouse, reports the relative movement of the horizontal scroll wheel. +The value is normalized to a range from -1.0 (left) to 1.0 (right). + +This axis should be used to scroll views horizontally.*/ +pub const AMOTION_EVENT_AXIS_HSCROLL: c_uint = 10; +/** Axis constant: Z axis of a motion event. + +- For a joystick, reports the absolute Z position of the joystick. +The value is normalized to a range from -1.0 (high) to 1.0 (low). +On game pads with two analog joysticks, this axis is often reinterpreted +to report the absolute X position of the second joystick instead.*/ +pub const AMOTION_EVENT_AXIS_Z: c_uint = 11; +/** Axis constant: X Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the X axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).*/ +pub const AMOTION_EVENT_AXIS_RX: c_uint = 12; +/** Axis constant: Y Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the Y axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise).*/ +pub const AMOTION_EVENT_AXIS_RY: c_uint = 13; +/** Axis constant: Z Rotation axis of a motion event. + +- For a joystick, reports the absolute rotation angle about the Z axis. +The value is normalized to a range from -1.0 (counter-clockwise) to 1.0 (clockwise). +On game pads with two analog joysticks, this axis is often reinterpreted +to report the absolute Y position of the second joystick instead.*/ +pub const AMOTION_EVENT_AXIS_RZ: c_uint = 14; +/** Axis constant: Hat X axis of a motion event. + +- For a joystick, reports the absolute X position of the directional hat control. +The value is normalized to a range from -1.0 (left) to 1.0 (right).*/ +pub const AMOTION_EVENT_AXIS_HAT_X: c_uint = 15; +/** Axis constant: Hat Y axis of a motion event. + +- For a joystick, reports the absolute Y position of the directional hat control. +The value is normalized to a range from -1.0 (up) to 1.0 (down).*/ +pub const AMOTION_EVENT_AXIS_HAT_Y: c_uint = 16; +/** Axis constant: Left Trigger axis of a motion event. + +- For a joystick, reports the absolute position of the left trigger control. +The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).*/ +pub const AMOTION_EVENT_AXIS_LTRIGGER: c_uint = 17; +/** Axis constant: Right Trigger axis of a motion event. + +- For a joystick, reports the absolute position of the right trigger control. +The value is normalized to a range from 0.0 (released) to 1.0 (fully pressed).*/ +pub const AMOTION_EVENT_AXIS_RTRIGGER: c_uint = 18; +/** Axis constant: Throttle axis of a motion event. + +- For a joystick, reports the absolute position of the throttle control. +The value is normalized to a range from 0.0 (fully open) to 1.0 (fully closed).*/ +pub const AMOTION_EVENT_AXIS_THROTTLE: c_uint = 19; +/** Axis constant: Rudder axis of a motion event. + +- For a joystick, reports the absolute position of the rudder control. +The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).*/ +pub const AMOTION_EVENT_AXIS_RUDDER: c_uint = 20; +/** Axis constant: Wheel axis of a motion event. + +- For a joystick, reports the absolute position of the steering wheel control. +The value is normalized to a range from -1.0 (turn left) to 1.0 (turn right).*/ +pub const AMOTION_EVENT_AXIS_WHEEL: c_uint = 21; +/** Axis constant: Gas axis of a motion event. + +- For a joystick, reports the absolute position of the gas (accelerator) control. +The value is normalized to a range from 0.0 (no acceleration) +to 1.0 (maximum acceleration).*/ +pub const AMOTION_EVENT_AXIS_GAS: c_uint = 22; +/** Axis constant: Brake axis of a motion event. + +- For a joystick, reports the absolute position of the brake control. +The value is normalized to a range from 0.0 (no braking) to 1.0 (maximum braking).*/ +pub const AMOTION_EVENT_AXIS_BRAKE: c_uint = 23; +/** Axis constant: Distance axis of a motion event. + +- For a stylus, reports the distance of the stylus from the screen. +A value of 0.0 indicates direct contact and larger values indicate increasing +distance from the surface.*/ +pub const AMOTION_EVENT_AXIS_DISTANCE: c_uint = 24; +/** Axis constant: Tilt axis of a motion event. + +- For a stylus, reports the tilt angle of the stylus in radians where +0 radians indicates that the stylus is being held perpendicular to the +surface, and PI/2 radians indicates that the stylus is being held flat +against the surface.*/ +pub const AMOTION_EVENT_AXIS_TILT: c_uint = 25; +/** Axis constant: Generic scroll axis of a motion event. + +- This is used for scroll axis motion events that can't be classified as strictly + vertical or horizontal. The movement of a rotating scroller is an example of this.*/ +pub const AMOTION_EVENT_AXIS_SCROLL: c_uint = 26; +/** Axis constant: The movement of x position of a motion event. + +- For a mouse, reports a difference of x position between the previous position. +This is useful when pointer is captured, in that case the mouse pointer doesn't +change the location but this axis reports the difference which allows the app +to see how the mouse is moved.*/ +pub const AMOTION_EVENT_AXIS_RELATIVE_X: c_uint = 27; +/** Axis constant: The movement of y position of a motion event. + +Same as #AMOTION_EVENT_AXIS_RELATIVE_X, but for y position.*/ +pub const AMOTION_EVENT_AXIS_RELATIVE_Y: c_uint = 28; +/** Axis constant: Generic 1 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_1: c_uint = 32; +/** Axis constant: Generic 2 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_2: c_uint = 33; +/** Axis constant: Generic 3 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_3: c_uint = 34; +/** Axis constant: Generic 4 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_4: c_uint = 35; +/** Axis constant: Generic 5 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_5: c_uint = 36; +/** Axis constant: Generic 6 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_6: c_uint = 37; +/** Axis constant: Generic 7 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_7: c_uint = 38; +/** Axis constant: Generic 8 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_8: c_uint = 39; +/** Axis constant: Generic 9 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_9: c_uint = 40; +/** Axis constant: Generic 10 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_10: c_uint = 41; +/** Axis constant: Generic 11 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_11: c_uint = 42; +/** Axis constant: Generic 12 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_12: c_uint = 43; +/** Axis constant: Generic 13 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_13: c_uint = 44; +/** Axis constant: Generic 14 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_14: c_uint = 45; +/** Axis constant: Generic 15 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_15: c_uint = 46; +/** Axis constant: Generic 16 axis of a motion event. +The interpretation of a generic axis is device-specific.*/ +pub const AMOTION_EVENT_AXIS_GENERIC_16: c_uint = 47; +/** Axis constant: X gesture offset axis of a motion event. + +- For a touch pad, reports the distance that a swipe gesture has moved in the X axis, as a + proportion of the touch pad's size. For example, if a touch pad is 1000 units wide, and a + swipe gesture starts at X = 500 then moves to X = 400, this axis would have a value of + -0.1. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_X_OFFSET: c_uint = 48; +/** Axis constant: Y gesture offset axis of a motion event. + +The same as {@link AMOTION_EVENT_AXIS_GESTURE_X_OFFSET}, but for the Y axis.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET: c_uint = 49; +/** Axis constant: X scroll distance axis of a motion event. + +- For a touch pad, reports the distance that should be scrolled in the X axis as a result of + the user's two-finger scroll gesture, in display pixels. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE: c_uint = 50; +/** Axis constant: Y scroll distance axis of a motion event. + +The same as {@link AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE}, but for the Y axis.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE: c_uint = 51; +/** Axis constant: pinch scale factor of a motion event. + +- For a touch pad, reports the change in distance between the fingers when the user is making + a pinch gesture, as a proportion of that distance when the gesture was last reported. For + example, if the fingers were 50 units apart and are now 52 units apart, the scale factor + would be 1.04. + +These values are relative to the state from the last event, not accumulated, so developers +should make sure to process this axis value for all batched historical events. + +This axis is only set on the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR: c_uint = 52; +/** Axis constant: the number of fingers being used in a multi-finger swipe gesture. + +- For a touch pad, reports the number of fingers being used in a multi-finger swipe gesture + (with CLASSIFICATION_MULTI_FINGER_SWIPE). + +Since CLASSIFICATION_MULTI_FINGER_SWIPE is a hidden API, so is this axis. It is only set on +the first pointer in a motion event.*/ +pub const AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT: c_uint = 53; + +/* These flags originate in RawEvents and are generally set in the key map. + * NOTE: If you want a flag to be able to set in a keylayout file, then you must add it to + * InputEventLabels.h as well. */ + +// Indicates that the event should wake the device. +pub const POLICY_FLAG_WAKE: c_uint = 0x00000001; + +// Indicates that the key is virtual, such as a capacitive button, and should +// generate haptic feedback. Virtual keys may be suppressed for some time +// after a recent touch to prevent accidental activation of virtual keys adjacent +// to the touch screen during an edge swipe. + +pub const POLICY_FLAG_VIRTUAL: c_uint = 0x00000002; + +// Indicates that the key is the special function modifier. +pub const POLICY_FLAG_FUNCTION: c_uint = 0x00000004; + +// Indicates that the key represents a special gesture that has been detected by +// the touch firmware or driver. Causes touch events from the same device to be canceled. +// This policy flag prevents key events from changing touch mode state. +pub const POLICY_FLAG_GESTURE: c_uint = 0x00000008; + +// Indicates that key usage mapping represents a fallback mapping. +// Fallback mappings cannot be used to definitively determine whether a device +// supports a key code. For example, a HID device can report a key press +// as a HID usage code if it is not mapped to any linux key code in the kernel. +// However, we cannot know which HID usage codes that device supports from +// userspace through the evdev. We can use fallback mappings to convert HID +// usage codes to Android key codes without needing to know if a device can +// actually report the usage code. +pub const POLICY_FLAG_FALLBACK_USAGE_MAPPING: c_uint = 0x00000010; diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs new file mode 100644 index 0000000000..ac7f35eb89 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/generic_key_layout.rs @@ -0,0 +1,288 @@ +//! Hardcoded Generic key layout map based on AOSP Generic.kl. +//! +//! This provides a fallback key layout when no device-specific or system Generic.kl +//! file is available. +//! +//! Source: https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/data/keyboards/Generic.kl + +/// Generic key layout content matching AOSP Generic.kl +pub const GENERIC_KEY_LAYOUT_CONTENTS: &str = r#" +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 55 NUMPAD_MULTIPLY +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 69 NUM_LOCK +key 70 SCROLL_LOCK +key 71 NUMPAD_7 +key 72 NUMPAD_8 +key 73 NUMPAD_9 +key 74 NUMPAD_SUBTRACT +key 75 NUMPAD_4 +key 76 NUMPAD_5 +key 77 NUMPAD_6 +key 78 NUMPAD_ADD +key 79 NUMPAD_1 +key 80 NUMPAD_2 +key 81 NUMPAD_3 +key 82 NUMPAD_0 +key 83 NUMPAD_DOT +key 85 ZENKAKU_HANKAKU +key 86 BACKSLASH +key 87 F11 +key 88 F12 +key 89 RO +key 92 HENKAN +key 93 KATAKANA_HIRAGANA +key 94 MUHENKAN +key 95 NUMPAD_COMMA +key 96 NUMPAD_ENTER +key 97 CTRL_RIGHT +key 98 NUMPAD_DIVIDE +key 99 SYSRQ +key 100 ALT_RIGHT +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 INSERT +key 111 FORWARD_DEL +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 116 POWER +key 117 NUMPAD_EQUALS +key 119 BREAK +key 120 RECENT_APPS +key 121 NUMPAD_COMMA +key 122 KANA +key 123 EISU +key 124 YEN +key 125 META_LEFT +key 126 META_RIGHT +key 127 MENU +key 128 MEDIA_STOP +key 133 COPY +key 135 PASTE +key 137 CUT +key 139 MENU +key 140 CALCULATOR +key 142 SLEEP +key 143 WAKEUP +key 150 EXPLORER +key 152 POWER +key 155 ENVELOPE +key 156 BOOKMARK +key 158 BACK +key 159 FORWARD +key 160 MEDIA_CLOSE +key 161 MEDIA_EJECT +key 162 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 169 CALL +key 171 MUSIC +key 172 HOME +key 173 REFRESH +key 177 PAGE_UP +key 178 PAGE_DOWN +key 179 NUMPAD_LEFT_PAREN +key 180 NUMPAD_RIGHT_PAREN +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +key 204 NOTIFICATION +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +key 212 CAMERA +key 213 MUSIC +key 215 ENVELOPE +key 217 SEARCH +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +key 226 HEADSETHOOK +key 228 KEYBOARD_BACKLIGHT_TOGGLE +key 229 KEYBOARD_BACKLIGHT_DOWN +key 230 KEYBOARD_BACKLIGHT_UP +key 248 MUTE +key 256 BUTTON_1 +key 257 BUTTON_2 +key 258 BUTTON_3 +key 259 BUTTON_4 +key 260 BUTTON_5 +key 261 BUTTON_6 +key 262 BUTTON_7 +key 263 BUTTON_8 +key 264 BUTTON_9 +key 265 BUTTON_10 +key 266 BUTTON_11 +key 267 BUTTON_12 +key 268 BUTTON_13 +key 269 BUTTON_14 +key 270 BUTTON_15 +key 271 BUTTON_16 +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_C +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_Z +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR +key 329 STYLUS_BUTTON_TERTIARY +key 331 STYLUS_BUTTON_PRIMARY +key 332 STYLUS_BUTTON_SECONDARY +key 353 DPAD_CENTER +key 362 GUIDE +key 366 DVR +key 370 CAPTIONS +key 377 TV +key 397 CALENDAR +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +key 405 LAST_CHANNEL +key 418 ZOOM_IN +key 419 ZOOM_OUT +key 429 CONTACTS +key 464 FUNCTION +key 465 ESCAPE FUNCTION +key 466 F1 FUNCTION +key 467 F2 FUNCTION +key 468 F3 FUNCTION +key 469 F4 FUNCTION +key 470 F5 FUNCTION +key 471 F6 FUNCTION +key 472 F7 FUNCTION +key 473 F8 FUNCTION +key 474 F9 FUNCTION +key 475 F10 FUNCTION +key 476 F11 FUNCTION +key 477 F12 FUNCTION +key 478 1 FUNCTION +key 479 2 FUNCTION +key 480 D FUNCTION +key 481 E FUNCTION +key 482 F FUNCTION +key 483 S FUNCTION +key 484 B FUNCTION +key 522 STAR +key 523 POUND +key 528 FOCUS +key 580 APP_SWITCH +key 582 VOICE_ASSIST +key 583 ASSIST +key 656 MACRO_1 +key 657 MACRO_2 +key 658 MACRO_3 +key 659 MACRO_4 +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x06 THROTTLE +axis 0x07 RUDDER +axis 0x08 WHEEL +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y +"#; diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs new file mode 100644 index 0000000000..c7db755926 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/input_event_lookup.rs @@ -0,0 +1,476 @@ +//! Input event label lookup functionality. +//! +//! Provides lookup functions for converting between string labels and numeric values +//! for Android key codes, flags, and axes. +//! +//! Uses Android NDK constants (AKEYCODE_*, AMOTION_EVENT_AXIS_*) from the generated bindings. +//! POLICY_FLAG_* constants are defined locally as they come from the local Input.h header. + +use crate::android::android_codes; +use std::collections::HashMap; +use std::sync::OnceLock; + +// Macro to define keycode entry using AKEYCODE_* constant +macro_rules! define_keycode { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Macro to define axis entry using AMOTION_EVENT_AXIS_* constant +macro_rules! define_axis { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Macro to define policy flag entry using POLICY_FLAG_* constant +macro_rules! define_flag { + ($name:ident) => { + (stringify!($name).to_string(), android_codes::$name as u32) + }; +} + +// Build the keycodes map +fn build_keycodes_map() -> HashMap { + let mut map = HashMap::new(); + + // Helper macro to insert keycode + macro_rules! insert_keycode { + ($name:ident) => { + let (name, value) = define_keycode!($name); + map.insert(name, value); + }; + } + + insert_keycode!(AKEYCODE_0); + insert_keycode!(AKEYCODE_1); + insert_keycode!(AKEYCODE_2); + insert_keycode!(AKEYCODE_3); + insert_keycode!(AKEYCODE_4); + insert_keycode!(AKEYCODE_5); + insert_keycode!(AKEYCODE_6); + insert_keycode!(AKEYCODE_7); + insert_keycode!(AKEYCODE_8); + insert_keycode!(AKEYCODE_9); + insert_keycode!(AKEYCODE_UNKNOWN); + insert_keycode!(AKEYCODE_SOFT_LEFT); + insert_keycode!(AKEYCODE_SOFT_RIGHT); + insert_keycode!(AKEYCODE_HOME); + insert_keycode!(AKEYCODE_BACK); + insert_keycode!(AKEYCODE_CALL); + insert_keycode!(AKEYCODE_ENDCALL); + insert_keycode!(AKEYCODE_STAR); + insert_keycode!(AKEYCODE_POUND); + insert_keycode!(AKEYCODE_DPAD_UP); + insert_keycode!(AKEYCODE_DPAD_DOWN); + insert_keycode!(AKEYCODE_DPAD_LEFT); + insert_keycode!(AKEYCODE_DPAD_RIGHT); + insert_keycode!(AKEYCODE_DPAD_CENTER); + insert_keycode!(AKEYCODE_VOLUME_UP); + insert_keycode!(AKEYCODE_VOLUME_DOWN); + insert_keycode!(AKEYCODE_POWER); + insert_keycode!(AKEYCODE_CAMERA); + insert_keycode!(AKEYCODE_CLEAR); + insert_keycode!(AKEYCODE_A); + insert_keycode!(AKEYCODE_B); + insert_keycode!(AKEYCODE_C); + insert_keycode!(AKEYCODE_D); + insert_keycode!(AKEYCODE_E); + insert_keycode!(AKEYCODE_F); + insert_keycode!(AKEYCODE_G); + insert_keycode!(AKEYCODE_H); + insert_keycode!(AKEYCODE_I); + insert_keycode!(AKEYCODE_J); + insert_keycode!(AKEYCODE_K); + insert_keycode!(AKEYCODE_L); + insert_keycode!(AKEYCODE_M); + insert_keycode!(AKEYCODE_N); + insert_keycode!(AKEYCODE_O); + insert_keycode!(AKEYCODE_P); + insert_keycode!(AKEYCODE_Q); + insert_keycode!(AKEYCODE_R); + insert_keycode!(AKEYCODE_S); + insert_keycode!(AKEYCODE_T); + insert_keycode!(AKEYCODE_U); + insert_keycode!(AKEYCODE_V); + insert_keycode!(AKEYCODE_W); + insert_keycode!(AKEYCODE_X); + insert_keycode!(AKEYCODE_Y); + insert_keycode!(AKEYCODE_Z); + insert_keycode!(AKEYCODE_COMMA); + insert_keycode!(AKEYCODE_PERIOD); + insert_keycode!(AKEYCODE_ALT_LEFT); + insert_keycode!(AKEYCODE_ALT_RIGHT); + insert_keycode!(AKEYCODE_SHIFT_LEFT); + insert_keycode!(AKEYCODE_SHIFT_RIGHT); + insert_keycode!(AKEYCODE_TAB); + insert_keycode!(AKEYCODE_SPACE); + insert_keycode!(AKEYCODE_SYM); + insert_keycode!(AKEYCODE_EXPLORER); + insert_keycode!(AKEYCODE_ENVELOPE); + insert_keycode!(AKEYCODE_ENTER); + insert_keycode!(AKEYCODE_DEL); + insert_keycode!(AKEYCODE_GRAVE); + insert_keycode!(AKEYCODE_MINUS); + insert_keycode!(AKEYCODE_EQUALS); + insert_keycode!(AKEYCODE_LEFT_BRACKET); + insert_keycode!(AKEYCODE_RIGHT_BRACKET); + insert_keycode!(AKEYCODE_BACKSLASH); + insert_keycode!(AKEYCODE_SEMICOLON); + insert_keycode!(AKEYCODE_APOSTROPHE); + insert_keycode!(AKEYCODE_SLASH); + insert_keycode!(AKEYCODE_AT); + insert_keycode!(AKEYCODE_NUM); + insert_keycode!(AKEYCODE_HEADSETHOOK); + insert_keycode!(AKEYCODE_FOCUS); + insert_keycode!(AKEYCODE_PLUS); + insert_keycode!(AKEYCODE_MENU); + insert_keycode!(AKEYCODE_NOTIFICATION); + insert_keycode!(AKEYCODE_SEARCH); + insert_keycode!(AKEYCODE_MEDIA_PLAY_PAUSE); + insert_keycode!(AKEYCODE_MEDIA_STOP); + insert_keycode!(AKEYCODE_MEDIA_NEXT); + insert_keycode!(AKEYCODE_MEDIA_PREVIOUS); + insert_keycode!(AKEYCODE_MEDIA_REWIND); + insert_keycode!(AKEYCODE_MEDIA_FAST_FORWARD); + insert_keycode!(AKEYCODE_MUTE); + insert_keycode!(AKEYCODE_PAGE_UP); + insert_keycode!(AKEYCODE_PAGE_DOWN); + insert_keycode!(AKEYCODE_PICTSYMBOLS); + insert_keycode!(AKEYCODE_SWITCH_CHARSET); + insert_keycode!(AKEYCODE_BUTTON_A); + insert_keycode!(AKEYCODE_BUTTON_B); + insert_keycode!(AKEYCODE_BUTTON_C); + insert_keycode!(AKEYCODE_BUTTON_X); + insert_keycode!(AKEYCODE_BUTTON_Y); + insert_keycode!(AKEYCODE_BUTTON_Z); + insert_keycode!(AKEYCODE_BUTTON_L1); + insert_keycode!(AKEYCODE_BUTTON_R1); + insert_keycode!(AKEYCODE_BUTTON_L2); + insert_keycode!(AKEYCODE_BUTTON_R2); + insert_keycode!(AKEYCODE_BUTTON_THUMBL); + insert_keycode!(AKEYCODE_BUTTON_THUMBR); + insert_keycode!(AKEYCODE_BUTTON_START); + insert_keycode!(AKEYCODE_BUTTON_SELECT); + insert_keycode!(AKEYCODE_BUTTON_MODE); + insert_keycode!(AKEYCODE_ESCAPE); + insert_keycode!(AKEYCODE_FORWARD_DEL); + insert_keycode!(AKEYCODE_CTRL_LEFT); + insert_keycode!(AKEYCODE_CTRL_RIGHT); + insert_keycode!(AKEYCODE_CAPS_LOCK); + insert_keycode!(AKEYCODE_SCROLL_LOCK); + insert_keycode!(AKEYCODE_META_LEFT); + insert_keycode!(AKEYCODE_META_RIGHT); + insert_keycode!(AKEYCODE_FUNCTION); + insert_keycode!(AKEYCODE_SYSRQ); + insert_keycode!(AKEYCODE_BREAK); + insert_keycode!(AKEYCODE_MOVE_HOME); + insert_keycode!(AKEYCODE_MOVE_END); + insert_keycode!(AKEYCODE_INSERT); + insert_keycode!(AKEYCODE_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_PLAY); + insert_keycode!(AKEYCODE_MEDIA_PAUSE); + insert_keycode!(AKEYCODE_MEDIA_CLOSE); + insert_keycode!(AKEYCODE_MEDIA_EJECT); + insert_keycode!(AKEYCODE_MEDIA_RECORD); + insert_keycode!(AKEYCODE_F1); + insert_keycode!(AKEYCODE_F2); + insert_keycode!(AKEYCODE_F3); + insert_keycode!(AKEYCODE_F4); + insert_keycode!(AKEYCODE_F5); + insert_keycode!(AKEYCODE_F6); + insert_keycode!(AKEYCODE_F7); + insert_keycode!(AKEYCODE_F8); + insert_keycode!(AKEYCODE_F9); + insert_keycode!(AKEYCODE_F10); + insert_keycode!(AKEYCODE_F11); + insert_keycode!(AKEYCODE_F12); + insert_keycode!(AKEYCODE_NUM_LOCK); + insert_keycode!(AKEYCODE_NUMPAD_0); + insert_keycode!(AKEYCODE_NUMPAD_1); + insert_keycode!(AKEYCODE_NUMPAD_2); + insert_keycode!(AKEYCODE_NUMPAD_3); + insert_keycode!(AKEYCODE_NUMPAD_4); + insert_keycode!(AKEYCODE_NUMPAD_5); + insert_keycode!(AKEYCODE_NUMPAD_6); + insert_keycode!(AKEYCODE_NUMPAD_7); + insert_keycode!(AKEYCODE_NUMPAD_8); + insert_keycode!(AKEYCODE_NUMPAD_9); + insert_keycode!(AKEYCODE_NUMPAD_DIVIDE); + insert_keycode!(AKEYCODE_NUMPAD_MULTIPLY); + insert_keycode!(AKEYCODE_NUMPAD_SUBTRACT); + insert_keycode!(AKEYCODE_NUMPAD_ADD); + insert_keycode!(AKEYCODE_NUMPAD_DOT); + insert_keycode!(AKEYCODE_NUMPAD_COMMA); + insert_keycode!(AKEYCODE_NUMPAD_ENTER); + insert_keycode!(AKEYCODE_NUMPAD_EQUALS); + insert_keycode!(AKEYCODE_NUMPAD_LEFT_PAREN); + insert_keycode!(AKEYCODE_NUMPAD_RIGHT_PAREN); + insert_keycode!(AKEYCODE_VOLUME_MUTE); + insert_keycode!(AKEYCODE_INFO); + insert_keycode!(AKEYCODE_CHANNEL_UP); + insert_keycode!(AKEYCODE_CHANNEL_DOWN); + insert_keycode!(AKEYCODE_ZOOM_IN); + insert_keycode!(AKEYCODE_ZOOM_OUT); + insert_keycode!(AKEYCODE_TV); + insert_keycode!(AKEYCODE_WINDOW); + insert_keycode!(AKEYCODE_GUIDE); + insert_keycode!(AKEYCODE_DVR); + insert_keycode!(AKEYCODE_BOOKMARK); + insert_keycode!(AKEYCODE_CAPTIONS); + insert_keycode!(AKEYCODE_SETTINGS); + insert_keycode!(AKEYCODE_TV_POWER); + insert_keycode!(AKEYCODE_TV_INPUT); + insert_keycode!(AKEYCODE_STB_POWER); + insert_keycode!(AKEYCODE_STB_INPUT); + insert_keycode!(AKEYCODE_AVR_POWER); + insert_keycode!(AKEYCODE_AVR_INPUT); + insert_keycode!(AKEYCODE_PROG_RED); + insert_keycode!(AKEYCODE_PROG_GREEN); + insert_keycode!(AKEYCODE_PROG_YELLOW); + insert_keycode!(AKEYCODE_PROG_BLUE); + insert_keycode!(AKEYCODE_APP_SWITCH); + insert_keycode!(AKEYCODE_BUTTON_1); + insert_keycode!(AKEYCODE_BUTTON_2); + insert_keycode!(AKEYCODE_BUTTON_3); + insert_keycode!(AKEYCODE_BUTTON_4); + insert_keycode!(AKEYCODE_BUTTON_5); + insert_keycode!(AKEYCODE_BUTTON_6); + insert_keycode!(AKEYCODE_BUTTON_7); + insert_keycode!(AKEYCODE_BUTTON_8); + insert_keycode!(AKEYCODE_BUTTON_9); + insert_keycode!(AKEYCODE_BUTTON_10); + insert_keycode!(AKEYCODE_BUTTON_11); + insert_keycode!(AKEYCODE_BUTTON_12); + insert_keycode!(AKEYCODE_BUTTON_13); + insert_keycode!(AKEYCODE_BUTTON_14); + insert_keycode!(AKEYCODE_BUTTON_15); + insert_keycode!(AKEYCODE_BUTTON_16); + insert_keycode!(AKEYCODE_LANGUAGE_SWITCH); + insert_keycode!(AKEYCODE_MANNER_MODE); + insert_keycode!(AKEYCODE_3D_MODE); + insert_keycode!(AKEYCODE_CONTACTS); + insert_keycode!(AKEYCODE_CALENDAR); + insert_keycode!(AKEYCODE_MUSIC); + insert_keycode!(AKEYCODE_CALCULATOR); + insert_keycode!(AKEYCODE_ZENKAKU_HANKAKU); + insert_keycode!(AKEYCODE_EISU); + insert_keycode!(AKEYCODE_MUHENKAN); + insert_keycode!(AKEYCODE_HENKAN); + insert_keycode!(AKEYCODE_KATAKANA_HIRAGANA); + insert_keycode!(AKEYCODE_YEN); + insert_keycode!(AKEYCODE_RO); + insert_keycode!(AKEYCODE_KANA); + insert_keycode!(AKEYCODE_ASSIST); + insert_keycode!(AKEYCODE_BRIGHTNESS_DOWN); + insert_keycode!(AKEYCODE_BRIGHTNESS_UP); + insert_keycode!(AKEYCODE_MEDIA_AUDIO_TRACK); + insert_keycode!(AKEYCODE_SLEEP); + insert_keycode!(AKEYCODE_WAKEUP); + insert_keycode!(AKEYCODE_PAIRING); + insert_keycode!(AKEYCODE_MEDIA_TOP_MENU); + insert_keycode!(AKEYCODE_LAST_CHANNEL); + insert_keycode!(AKEYCODE_TV_DATA_SERVICE); + insert_keycode!(AKEYCODE_VOICE_ASSIST); + insert_keycode!(AKEYCODE_TV_RADIO_SERVICE); + insert_keycode!(AKEYCODE_TV_TELETEXT); + insert_keycode!(AKEYCODE_TV_NUMBER_ENTRY); + insert_keycode!(AKEYCODE_TV_TERRESTRIAL_ANALOG); + insert_keycode!(AKEYCODE_TV_TERRESTRIAL_DIGITAL); + insert_keycode!(AKEYCODE_TV_SATELLITE); + insert_keycode!(AKEYCODE_TV_SATELLITE_BS); + insert_keycode!(AKEYCODE_TV_SATELLITE_CS); + insert_keycode!(AKEYCODE_TV_SATELLITE_SERVICE); + insert_keycode!(AKEYCODE_TV_NETWORK); + insert_keycode!(AKEYCODE_TV_ANTENNA_CABLE); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_1); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_2); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_3); + insert_keycode!(AKEYCODE_TV_INPUT_HDMI_4); + insert_keycode!(AKEYCODE_TV_INPUT_COMPOSITE_1); + insert_keycode!(AKEYCODE_TV_INPUT_COMPOSITE_2); + insert_keycode!(AKEYCODE_TV_INPUT_COMPONENT_1); + insert_keycode!(AKEYCODE_TV_INPUT_COMPONENT_2); + insert_keycode!(AKEYCODE_TV_INPUT_VGA_1); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_UP); + insert_keycode!(AKEYCODE_TV_AUDIO_DESCRIPTION_MIX_DOWN); + insert_keycode!(AKEYCODE_TV_ZOOM_MODE); + insert_keycode!(AKEYCODE_TV_CONTENTS_MENU); + insert_keycode!(AKEYCODE_TV_MEDIA_CONTEXT_MENU); + insert_keycode!(AKEYCODE_TV_TIMER_PROGRAMMING); + insert_keycode!(AKEYCODE_HELP); + insert_keycode!(AKEYCODE_NAVIGATE_PREVIOUS); + insert_keycode!(AKEYCODE_NAVIGATE_NEXT); + insert_keycode!(AKEYCODE_NAVIGATE_IN); + insert_keycode!(AKEYCODE_NAVIGATE_OUT); + insert_keycode!(AKEYCODE_STEM_PRIMARY); + insert_keycode!(AKEYCODE_STEM_1); + insert_keycode!(AKEYCODE_STEM_2); + insert_keycode!(AKEYCODE_STEM_3); + insert_keycode!(AKEYCODE_DPAD_UP_LEFT); + insert_keycode!(AKEYCODE_DPAD_DOWN_LEFT); + insert_keycode!(AKEYCODE_DPAD_UP_RIGHT); + insert_keycode!(AKEYCODE_DPAD_DOWN_RIGHT); + insert_keycode!(AKEYCODE_MEDIA_SKIP_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_SKIP_BACKWARD); + insert_keycode!(AKEYCODE_MEDIA_STEP_FORWARD); + insert_keycode!(AKEYCODE_MEDIA_STEP_BACKWARD); + insert_keycode!(AKEYCODE_SOFT_SLEEP); + insert_keycode!(AKEYCODE_CUT); + insert_keycode!(AKEYCODE_COPY); + insert_keycode!(AKEYCODE_PASTE); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_UP); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_DOWN); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_LEFT); + insert_keycode!(AKEYCODE_SYSTEM_NAVIGATION_RIGHT); + insert_keycode!(AKEYCODE_ALL_APPS); + insert_keycode!(AKEYCODE_REFRESH); + insert_keycode!(AKEYCODE_THUMBS_UP); + insert_keycode!(AKEYCODE_THUMBS_DOWN); + insert_keycode!(AKEYCODE_PROFILE_SWITCH); + insert_keycode!(AKEYCODE_VIDEO_APP_1); + insert_keycode!(AKEYCODE_VIDEO_APP_2); + insert_keycode!(AKEYCODE_VIDEO_APP_3); + insert_keycode!(AKEYCODE_VIDEO_APP_4); + insert_keycode!(AKEYCODE_VIDEO_APP_5); + insert_keycode!(AKEYCODE_VIDEO_APP_6); + insert_keycode!(AKEYCODE_VIDEO_APP_7); + insert_keycode!(AKEYCODE_VIDEO_APP_8); + insert_keycode!(AKEYCODE_FEATURED_APP_1); + insert_keycode!(AKEYCODE_FEATURED_APP_2); + insert_keycode!(AKEYCODE_FEATURED_APP_3); + insert_keycode!(AKEYCODE_FEATURED_APP_4); + insert_keycode!(AKEYCODE_DEMO_APP_1); + insert_keycode!(AKEYCODE_DEMO_APP_2); + insert_keycode!(AKEYCODE_DEMO_APP_3); + insert_keycode!(AKEYCODE_DEMO_APP_4); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_DOWN); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_UP); + insert_keycode!(AKEYCODE_KEYBOARD_BACKLIGHT_TOGGLE); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_PRIMARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_SECONDARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_TERTIARY); + insert_keycode!(AKEYCODE_STYLUS_BUTTON_TAIL); + insert_keycode!(AKEYCODE_RECENT_APPS); + insert_keycode!(AKEYCODE_MACRO_1); + insert_keycode!(AKEYCODE_MACRO_2); + insert_keycode!(AKEYCODE_MACRO_3); + insert_keycode!(AKEYCODE_MACRO_4); + + map +} + +// Build the axes map +fn build_axes_map() -> HashMap { + let mut map = HashMap::new(); + macro_rules! insert_axis { + ($name:ident) => { + let (name, value) = define_axis!($name); + map.insert(name, value); + }; + } + insert_axis!(AMOTION_EVENT_AXIS_X); + insert_axis!(AMOTION_EVENT_AXIS_Y); + insert_axis!(AMOTION_EVENT_AXIS_PRESSURE); + insert_axis!(AMOTION_EVENT_AXIS_SIZE); + insert_axis!(AMOTION_EVENT_AXIS_TOUCH_MAJOR); + insert_axis!(AMOTION_EVENT_AXIS_TOUCH_MINOR); + insert_axis!(AMOTION_EVENT_AXIS_TOOL_MAJOR); + insert_axis!(AMOTION_EVENT_AXIS_TOOL_MINOR); + insert_axis!(AMOTION_EVENT_AXIS_ORIENTATION); + insert_axis!(AMOTION_EVENT_AXIS_VSCROLL); + insert_axis!(AMOTION_EVENT_AXIS_HSCROLL); + insert_axis!(AMOTION_EVENT_AXIS_Z); + insert_axis!(AMOTION_EVENT_AXIS_RX); + insert_axis!(AMOTION_EVENT_AXIS_RY); + insert_axis!(AMOTION_EVENT_AXIS_RZ); + insert_axis!(AMOTION_EVENT_AXIS_HAT_X); + insert_axis!(AMOTION_EVENT_AXIS_HAT_Y); + insert_axis!(AMOTION_EVENT_AXIS_LTRIGGER); + insert_axis!(AMOTION_EVENT_AXIS_RTRIGGER); + insert_axis!(AMOTION_EVENT_AXIS_THROTTLE); + insert_axis!(AMOTION_EVENT_AXIS_RUDDER); + insert_axis!(AMOTION_EVENT_AXIS_WHEEL); + insert_axis!(AMOTION_EVENT_AXIS_GAS); + insert_axis!(AMOTION_EVENT_AXIS_BRAKE); + insert_axis!(AMOTION_EVENT_AXIS_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_TILT); + insert_axis!(AMOTION_EVENT_AXIS_SCROLL); + insert_axis!(AMOTION_EVENT_AXIS_RELATIVE_X); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_1); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_2); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_3); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_4); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_5); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_6); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_7); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_8); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_9); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_10); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_11); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_12); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_13); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_14); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_15); + insert_axis!(AMOTION_EVENT_AXIS_GENERIC_16); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_X_OFFSET); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_Y_OFFSET); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SCROLL_X_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SCROLL_Y_DISTANCE); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_PINCH_SCALE_FACTOR); + insert_axis!(AMOTION_EVENT_AXIS_GESTURE_SWIPE_FINGER_COUNT); + map +} + +// Build the flags map +fn build_flags_map() -> HashMap { + let mut map = HashMap::new(); + macro_rules! insert_flag { + ($name:ident) => { + let (name, value) = define_flag!($name); + map.insert(name, value); + }; + } + insert_flag!(POLICY_FLAG_VIRTUAL); + insert_flag!(POLICY_FLAG_FUNCTION); + insert_flag!(POLICY_FLAG_GESTURE); + insert_flag!(POLICY_FLAG_WAKE); + insert_flag!(POLICY_FLAG_FALLBACK_USAGE_MAPPING); + map +} + +// Static lookup tables (lazily initialized) +static KEYCODES: OnceLock> = OnceLock::new(); +static AXES: OnceLock> = OnceLock::new(); +static FLAGS: OnceLock> = OnceLock::new(); + +fn get_keycodes() -> &'static HashMap { + KEYCODES.get_or_init(build_keycodes_map) +} + +fn get_axes() -> &'static HashMap { + AXES.get_or_init(build_axes_map) +} + +fn get_flags() -> &'static HashMap { + FLAGS.get_or_init(build_flags_map) +} + +/// Look up a key code by its label. +pub fn get_key_code_by_label(label: &str) -> Option { + get_keycodes().get(&format!("AKEYCODE_{}", label)).copied() +} + +/// Look up an axis by its label. +pub fn get_axis_by_label(label: &str) -> Option { + get_axes() + .get(&format!("AMOTION_EVENT_AXIS_{}", label)) + .copied() +} + +/// Look up a key flag by its label. +pub fn get_key_flag_by_label(label: &str) -> Option { + get_flags().get(&format!("POLICY_FLAG_{}", label)).copied() +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_file_finder.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_file_finder.rs new file mode 100644 index 0000000000..52c1c9e8c3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_file_finder.rs @@ -0,0 +1,56 @@ +use std::{ + fs::{self, File}, + io::ErrorKind, + path::PathBuf, +}; + +/// Trait for finding key layout files. +/// This allows dependency injection for testing purposes. +pub trait KeyLayoutFileFinder: Send + Sync { + /// Find a key layout file in the system by its name. + fn find_system_key_layout_file_by_name(&self, name: &str) -> Option; + + /// Find a key layout file shipped with Key Mapper by its name. + fn find_key_mapper_key_layout_file_by_name(&self, name: &str) -> Option; +} + +/// Default implementation that uses the real file system. +/// This searches the standard Android key layout file locations. +pub struct AndroidKeyLayoutFileFinder; + +impl KeyLayoutFileFinder for AndroidKeyLayoutFileFinder { + fn find_system_key_layout_file_by_name(&self, name: &str) -> Option { + // See https://source.android.com/docs/core/interaction/input/key-layout-files#location + let path_prefixes = vec![ + "/odm/usr/".to_string(), + "/vendor/usr/".to_string(), + "/system/usr/".to_string(), + "/data/system/devices/".to_string(), + ]; + + for prefix in &path_prefixes { + let path = PathBuf::new() + .join(prefix) + .join("keylayout") + .join(format!("{}.kl", name)); + + match fs::metadata(&path) { + Ok(metadata) if metadata.is_file() => { + if File::open(&path).is_ok() { + return Some(path); + } + } + Err(e) if e.kind() != ErrorKind::NotFound => { + debug!("Error accessing {:?}: {}", path, e); + } + _ => {} + } + } + + None + } + + fn find_key_mapper_key_layout_file_by_name(&self, _name: &str) -> Option { + None + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs new file mode 100644 index 0000000000..cc2be6cb88 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map.rs @@ -0,0 +1,403 @@ +//! Key layout map parser for Android key layout files. +//! +//! This module provides functionality to parse Android key layout (.kl) files +//! and map scan codes to Android key codes. +//! +//! AOSP keylayout files can be found at: +//! https://cs.android.com/android/platform/superproject/+/android-latest-release:frameworks/base/data/keyboards/ + +use crate::android::keylayout::input_event_lookup::{get_axis_by_label, get_key_code_by_label}; +use crate::android::keylayout::tokenizer::Tokenizer; +use std::collections::HashMap; +use std::fmt; +use std::path::PathBuf; + +/// Describes a mapping from keyboard scan codes to Android key codes. +/// +/// This object is immutable after it has been loaded. +pub struct KeyLayoutMap { + pub scan_codes: Vec, + /// Maps scan codes to key codes. + keys_by_scan_code: HashMap, + /// Maps key codes to their corresponding scan codes (reverse lookup). + scan_codes_by_key_code: HashMap, + axes: HashMap, +} + +/// Represents axis information for joystick/gamepad axes. +#[derive(Debug, Clone)] +pub struct KeyLayoutAxisInfo { + pub mode: KeyLayoutAxisMode, + pub axis: u32, + pub high_axis: Option, + pub split_value: Option, + pub flat_override: Option, +} + +/// Axis mapping mode. +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum KeyLayoutAxisMode { + /// Axis value is reported directly. + Normal, + /// Axis value should be inverted before reporting. + Invert, + /// Axis value should be split into two axes. + Split, +} + +const WHITESPACE: &str = " \t\r"; + +impl KeyLayoutMap { + /// Load a key layout map from a file path. + pub fn load_from_file(file_path: PathBuf) -> Result { + let tokenizer = Tokenizer::from_file(file_path)?; + Self::load(tokenizer) + } + + /// Load a key layout map from file contents (useful for testing). + pub fn load_from_contents(contents: &str) -> Result { + let tokenizer = Tokenizer::from_contents(PathBuf::new(), contents); + Self::load(tokenizer) + } + + fn load(mut tokenizer: Tokenizer) -> Result { + let mut map = KeyLayoutMap { + scan_codes: Vec::with_capacity(16), + keys_by_scan_code: HashMap::new(), + scan_codes_by_key_code: HashMap::new(), + axes: HashMap::new(), + }; + + let mut parser = Parser::new(&mut map, &mut tokenizer); + parser + .parse() + .inspect_err(|err| error!("Failed to parse key layout map: {:?}", err))?; + + Ok(map) + } + + /// Map a scan code to an Android key code. + /// + /// Returns `Some(key_code)` on success, or `None` if not found. + pub fn map_key(&self, scan_code: u32) -> Option { + self.keys_by_scan_code.get(&scan_code).copied() + } + + /// Map a scan code to axis information. + /// + /// Returns `Some(axis_info)` if the scan code maps to an axis, or `None` if not found. + pub fn map_axis(&self, scan_code: u32) -> Option { + self.axes.get(&scan_code).cloned() + } + + /// Find all scan codes that map to the given key code. + pub fn find_scan_code_for_key(&self, key_code: u32) -> Option { + self.scan_codes_by_key_code.get(&key_code).cloned() + } +} + +impl fmt::Debug for KeyLayoutMap { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + let mut debug_struct = f.debug_struct("KeyLayoutMap"); + debug_struct.field("keys_count", &self.keys_by_scan_code.len()); + debug_struct.field("axes_count", &self.axes.len()); + + // Show a sample of keys (first 5) if there are any + if !self.keys_by_scan_code.is_empty() { + let sample_keys: Vec<_> = self + .keys_by_scan_code + .iter() + .take(5) + .map(|(scan_code, key_code)| (*scan_code, *key_code)) + .collect(); + debug_struct.field("sample_keys", &sample_keys); + } + + // Show a sample of axes (first 5) if there are any + if !self.axes.is_empty() { + let sample_axes: Vec<_> = self.axes.iter().take(5).collect(); + debug_struct.field("sample_axes", &sample_axes); + } + + debug_struct.finish() + } +} + +/// Parser for key layout map files. +struct Parser<'a> { + map: &'a mut KeyLayoutMap, + tokenizer: &'a mut Tokenizer, +} + +impl<'a> Parser<'a> { + fn new(map: &'a mut KeyLayoutMap, tokenizer: &'a mut Tokenizer) -> Self { + Self { map, tokenizer } + } + + fn parse(&mut self) -> Result<(), String> { + while !self.tokenizer.is_eof() { + self.tokenizer.skip_delimiters(WHITESPACE); + + if !self.tokenizer.is_eol() && self.tokenizer.peek_char() != '#' { + let keyword_token = self.tokenizer.next_token(WHITESPACE); + let mut skipped_line: bool = false; + + match keyword_token.as_str() { + "key" => { + self.tokenizer.skip_delimiters(WHITESPACE); + let code_token = self.tokenizer.next_token(WHITESPACE); + + // Skip "usage" entries - we only support scan codes + if code_token == "usage" { + skipped_line = true; + } else { + self.parse_key(&code_token)?; + } + } + "axis" => { + self.tokenizer.skip_delimiters(WHITESPACE); + self.parse_axis()?; + } + "led" | "sensor" => { + // Skip LEDs and sensors, we don't need them + self.tokenizer.next_line(); + continue; + } + "requires_kernel_config" => { + self.tokenizer.next_line(); + continue; + } + "usage" => { + // Skip usage code entries - evdev only provides scan codes + self.tokenizer.next_line(); + continue; + } + _ => { + return Err(format!( + "{}: Expected keyword, got '{}'.", + self.tokenizer.get_location(), + keyword_token + )); + } + } + + self.tokenizer.skip_delimiters(WHITESPACE); + + if !skipped_line && !self.tokenizer.is_eol() && self.tokenizer.peek_char() != '#' { + return Err(format!( + "{}: Expected end of line or trailing comment, got '{}'.", + self.tokenizer.get_location(), + self.tokenizer.peek_remainder_of_line() + )); + } + } + + self.tokenizer.next_line(); + } + + Ok(()) + } + + fn parse_key(&mut self, code_token: &str) -> Result<(), String> { + let scan_code = parse_int(code_token).ok_or_else(|| { + format!( + "{}: Expected key scan code number, got '{}'.", + self.tokenizer.get_location(), + code_token + ) + })?; + + if scan_code < 0 { + return Err(format!( + "{} is not a valid key scan code. Negative numbers are not allowed.", + scan_code + )); + } + + if self.map.keys_by_scan_code.contains_key(&(scan_code as u32)) { + return Err(format!( + "{}: Duplicate entry for key scan code '{}'.", + self.tokenizer.get_location(), + code_token + )); + } + + self.tokenizer.skip_delimiters(WHITESPACE); + let key_code_token = self.tokenizer.next_token(WHITESPACE); + let key_code = get_key_code_by_label(&key_code_token); + + // Skip any remaining tokens on the line (flags, etc.) + loop { + self.tokenizer.skip_delimiters(WHITESPACE); + if self.tokenizer.is_eol() || self.tokenizer.peek_char() == '#' { + break; + } + // Consume and skip the token (flags are ignored) + self.tokenizer.next_token(WHITESPACE); + } + + // Only insert if the key code is known + if let Some(key_code) = key_code { + self.map.scan_codes.push(scan_code as u32); + self.map + .keys_by_scan_code + .insert(scan_code as u32, key_code); + + // Only insert if this key_code doesn't already have a scan code. + // This ensures we keep the first (typically non-FUNCTION) scan code. + self.map + .scan_codes_by_key_code + .entry(key_code) + .or_insert(scan_code as u32); + } + + Ok(()) + } + + fn parse_axis(&mut self) -> Result<(), String> { + let scan_code_token = self.tokenizer.next_token(WHITESPACE); + let scan_code = parse_int(&scan_code_token).ok_or_else(|| { + format!( + "{}: Expected axis scan code number, got '{}'.", + self.tokenizer.get_location(), + scan_code_token + ) + })?; + + if scan_code < 0 { + return Err(format!( + "{} is not a valid key scan code for an axis. Negative numbers are not allowed.", + scan_code + )); + } + + if self.map.axes.contains_key(&(scan_code as u32)) { + return Err(format!( + "{}: Duplicate entry for axis scan code '{}'.", + self.tokenizer.get_location(), + scan_code_token + )); + } + + let mut axis_mode: KeyLayoutAxisMode = KeyLayoutAxisMode::Normal; + let axis: u32; + let mut split_value: Option = None; + let mut high_axis: Option = None; + let mut flat_override: Option = None; + + self.tokenizer.skip_delimiters(WHITESPACE); + let token = self.tokenizer.next_token(WHITESPACE); + + if token == "invert" { + axis_mode = KeyLayoutAxisMode::Invert; + + self.tokenizer.skip_delimiters(WHITESPACE); + + let axis_token = self.tokenizer.next_token(WHITESPACE); + + axis = get_axis_by_label(&axis_token).ok_or_else(|| { + format!( + "{}: Expected inverted axis label, got '{}'.", + self.tokenizer.get_location(), + axis_token + ) + })?; + } else if token == "split" { + axis_mode = KeyLayoutAxisMode::Split; + + self.tokenizer.skip_delimiters(WHITESPACE); + let split_token = self.tokenizer.next_token(WHITESPACE); + let split_value_raw = parse_int(&split_token).ok_or_else(|| { + format!( + "{}: Expected split value, got '{}'.", + self.tokenizer.get_location(), + split_token + ) + })?; + split_value = Some(split_value_raw); + + self.tokenizer.skip_delimiters(WHITESPACE); + let low_axis_token = self.tokenizer.next_token(WHITESPACE); + axis = get_axis_by_label(&low_axis_token).ok_or_else(|| { + format!( + "{}: Expected low axis label, got '{}'.", + self.tokenizer.get_location(), + low_axis_token + ) + })?; + + self.tokenizer.skip_delimiters(WHITESPACE); + let high_axis_token = self.tokenizer.next_token(WHITESPACE); + let high_axis_raw = get_axis_by_label(&high_axis_token).ok_or_else(|| { + format!( + "{}: Expected high axis label, got '{}'.", + self.tokenizer.get_location(), + high_axis_token + ) + })?; + high_axis = Some(high_axis_raw); + } else { + axis = get_axis_by_label(&token).ok_or_else(|| { + format!( + "{}: Expected axis label, 'split' or 'invert', got '{}'.", + self.tokenizer.get_location(), + token + ) + })?; + } + + loop { + self.tokenizer.skip_delimiters(WHITESPACE); + if self.tokenizer.is_eol() || self.tokenizer.peek_char() == '#' { + break; + } + + let keyword_token = self.tokenizer.next_token(WHITESPACE); + if keyword_token == "flat" { + self.tokenizer.skip_delimiters(WHITESPACE); + let flat_token = self.tokenizer.next_token(WHITESPACE); + let flat_override_raw = parse_int(&flat_token).ok_or_else(|| { + format!( + "{}: Expected flat value, got '{}'.", + self.tokenizer.get_location(), + flat_token + ) + })?; + flat_override = Some(flat_override_raw); + } else { + return Err(format!( + "{}: Expected keyword 'flat', got '{}'.", + self.tokenizer.get_location(), + keyword_token + )); + } + } + + let axis_info: KeyLayoutAxisInfo = KeyLayoutAxisInfo { + mode: axis_mode, + axis, + high_axis, + split_value, + flat_override, + }; + + self.map.axes.insert(scan_code as u32, axis_info); + Ok(()) + } +} + +/// Parse an integer from a string (supports decimal, hex with 0x prefix, and octal with 0 prefix). +pub fn parse_int(s: &str) -> Option { + if s.is_empty() { + return None; + } + + // Handle hex (0x prefix) and octal (0 prefix) + if s.starts_with("0x") || s.starts_with("0X") { + i32::from_str_radix(&s[2..], 16).ok() + } else if s.starts_with('0') && s.len() > 1 { + i32::from_str_radix(&s[1..], 8).ok() + } else { + s.parse::().ok() + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs new file mode 100644 index 0000000000..632c72d970 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/key_layout_map_manager.rs @@ -0,0 +1,249 @@ +use crate::android::keylayout::generic_key_layout::GENERIC_KEY_LAYOUT_CONTENTS; +use crate::android::keylayout::key_layout_file_finder::{ + AndroidKeyLayoutFileFinder, KeyLayoutFileFinder, +}; +use crate::android::keylayout::key_layout_map::KeyLayoutMap; +use crate::evdev_device_info::EvdevDeviceInfo; +use evdev::enums::{EventCode, EventType}; +use evdev::util::int_to_event_code; +use libc::c_uint; +use log::{error, info}; +use std::collections::HashMap; +use std::error::Error; +use std::path::PathBuf; +use std::sync::{Arc, Mutex, OnceLock}; + +static KEY_LAYOUT_MANAGER: OnceLock> = OnceLock::new(); +static GENERIC_KEY_LAYOUT_MAP: OnceLock> = OnceLock::new(); + +/// Get the static generic KeyLayoutMap instance. +/// This is lazily initialized from hardcoded key mappings based on AOSP Generic.kl. +pub fn get_generic_key_layout_map() -> Arc { + Arc::clone(GENERIC_KEY_LAYOUT_MAP.get_or_init(|| { + Arc::new( + KeyLayoutMap::load_from_contents(GENERIC_KEY_LAYOUT_CONTENTS) + .expect("Failed to parse hardcoded Generic key layout"), + ) + })) +} + +/// Manages KeyLayoutMap caching and key code mapping +/// This is the only file that directly interacts with KeyLayoutMap C bindings +/// and the only file that finds key layout file paths +pub struct KeyLayoutMapManager { + /// KeyLayoutMap cache + /// Maps device path to KeyLayoutMap handle. If the value is None then + /// the key layout map could not be found or there was an error parsing, + /// and it shouldn't be attempted again. + pub key_layout_maps: Mutex>>>, + /// File finder for locating key layout files + file_finder: Arc, +} + +impl KeyLayoutMapManager { + pub fn get() -> Arc { + Arc::clone(KEY_LAYOUT_MANAGER.get_or_init(|| Arc::new(Self::new()))) + } + + fn new() -> Self { + Self { + key_layout_maps: Mutex::new(HashMap::with_capacity(32)), + file_finder: Arc::new(AndroidKeyLayoutFileFinder), + } + } + + /// Create a new instance with a custom file finder. + /// This is primarily useful for testing. + pub fn with_file_finder(file_finder: Arc) -> Self { + Self { + key_layout_maps: Mutex::new(HashMap::with_capacity(32)), + file_finder, + } + } + + /// Map a raw evdev key code to Android key code. + /// Returns the android keycode if the key is found in the device's map, + /// falling back to the generic key layout if not found. + pub fn map_key( + &self, + device_info: &EvdevDeviceInfo, + scan_code: u32, + ) -> Result, Box> { + let device_map = self.get_key_layout_map_lazy(device_info)?; + + if let Some(map) = device_map { + if let Some(key_code) = map.map_key(scan_code) { + return Ok(Some(key_code)); + } + } + + // Fall back to generic key layout + Ok(get_generic_key_layout_map().map_key(scan_code)) + } + + /// Find the scan code for a given Android key code. + /// Returns the scan code if found in the device's map, + /// falling back to the generic key layout if not found. + pub fn find_scan_code_for_key( + &self, + device_info: &EvdevDeviceInfo, + key_code: u32, + ) -> Result, Box> { + let device_map = self.get_key_layout_map_lazy(device_info)?; + + if let Some(map) = device_map { + if let Some(scan_code) = map.find_scan_code_for_key(key_code) { + return Ok(Some(scan_code)); + } + } + + // Fall back to generic key layout + Ok(get_generic_key_layout_map().find_scan_code_for_key(key_code)) + } + + pub fn preload_key_layout_map( + &self, + device_info: &EvdevDeviceInfo, + ) -> Result>, Box> { + self.get_key_layout_map_lazy(device_info) + .inspect_err(|err| { + error!( + "Error preloading key layout map for device {}: {}", + device_info.name, err + ) + }) + } + + /// Get or load a key layout map for the given device identifier. + /// This method is public for testing purposes. + fn get_key_layout_map_lazy( + &self, + device_info: &EvdevDeviceInfo, + ) -> Result>, Box> { + let mut key_layout_maps = self.key_layout_maps.lock().unwrap(); + + if let Some(key_layout_map) = key_layout_maps.get(device_info) { + return Ok(key_layout_map.clone()); + } + + let key_layout_map_paths = self.find_key_layout_files(device_info); + info!( + "Found key layout map files for device {}: {:?}", + device_info.name, key_layout_map_paths + ); + + for path in key_layout_map_paths { + match KeyLayoutMap::load_from_file(path) { + Ok(key_layout_map) => { + let option = Some(Arc::new(key_layout_map)); + key_layout_maps.insert(device_info.clone(), option.clone()); + return Ok(option); + } + Err(e) => { + error!("Error parsing key layout map: {}", e); + // Continue to try the next file instead of failing immediately + } + } + } + + // No key layout map files were found or parsed successfully. + // Fall back to the hardcoded generic key layout map. + info!( + "No key layout files found for device {}, using hardcoded Generic fallback", + device_info.name + ); + let fallback = Some(get_generic_key_layout_map()); + key_layout_maps.insert(device_info.clone(), fallback.clone()); + Ok(fallback) + } + + /// Find all the possible key layout files to use for a device ordered by their priority. + /// A list is returned so there are fallback key layout files if one can't be parsed + /// for whatever reason. + /// Tries multiple naming schemes based on vendor/product/version, then device name, then Generic. + /// It first tries searching the system for the file, and then does the search again + /// in the files shipped with Key Mapper. + /// + /// See https://source.android.com/docs/core/interaction/input/key-layout-files#location + pub fn find_key_layout_files(&self, device_info: &EvdevDeviceInfo) -> Vec { + let name = device_info.name.as_str(); + let vendor = device_info.vendor; + let product = device_info.product; + let version = device_info.version; + + let mut paths: Vec = Vec::new(); + + // Try vendor/product/version path first + if vendor != 0 && product != 0 { + if version != 0 { + let version_name = format!( + "Vendor_{:04x}_Product_{:04x}_Version_{:04x}", + vendor, product, version + ); + if let Some(path) = self + .file_finder + .find_system_key_layout_file_by_name(&version_name) + { + paths.push(path); + } + } + + // Try vendor/product + let product_name = format!("Vendor_{:04x}_Product_{:04x}", vendor, product); + if let Some(path) = self + .file_finder + .find_system_key_layout_file_by_name(&product_name) + { + paths.push(path); + } + } + + // Try device name (canonical) + let canonical_name = get_canonical_name(name); + if let Some(path) = self + .file_finder + .find_system_key_layout_file_by_name(&canonical_name) + { + paths.push(path); + } + + // Try system generic + if let Some(path) = self + .file_finder + .find_system_key_layout_file_by_name("Generic") + { + paths.push(path); + } + + paths + } + + pub fn map_key_codes_to_event_codes(key_codes: &[u32]) -> Vec { + let generic_key_layout = get_generic_key_layout_map(); + + key_codes + .iter() + .filter_map(|key_code| generic_key_layout.find_scan_code_for_key(*key_code)) + .map(|scan_code| int_to_event_code(EventType::EV_KEY as c_uint, scan_code)) + .collect() + } +} + +impl Default for KeyLayoutMapManager { + fn default() -> Self { + Self::new() + } +} + +/// Get canonical name with all invalid characters replaced by underscores +fn get_canonical_name(name: &str) -> String { + name.chars() + .map(|ch| { + if ch.is_ascii_alphanumeric() || ch == '-' || ch == '_' { + ch + } else { + '_' + } + }) + .collect() +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs new file mode 100644 index 0000000000..fa19befdd8 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/mod.rs @@ -0,0 +1,6 @@ +pub mod generic_key_layout; +pub mod input_event_lookup; +pub mod key_layout_file_finder; +pub mod key_layout_map; +pub mod key_layout_map_manager; +pub mod tokenizer; diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs new file mode 100644 index 0000000000..a4b39c0a2b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/keylayout/tokenizer.rs @@ -0,0 +1,135 @@ +//! Simple tokenizer for parsing key layout map files. +//! +//! This tokenizer tracks position in a buffer and provides methods for +//! reading tokens, characters, and navigating through lines. + +use std::path::PathBuf; +use std::str; + +/// Simple tokenizer for parsing ASCII text files line by line. +pub struct Tokenizer { + file_path: PathBuf, + buffer: String, + current: usize, + line_number: usize, +} + +impl Tokenizer { + /// Create a tokenizer from file contents. + pub fn from_contents(file_path: PathBuf, contents: &str) -> Self { + Self { + file_path, + buffer: contents.to_string(), + current: 0, + line_number: 1, + } + } + + /// Create a tokenizer from a file path. + pub fn from_file(file_path: PathBuf) -> Result { + let contents = std::fs::read_to_string(file_path.clone()) + .map_err(|e| format!("Error opening file '{:?}': {}", file_path.clone(), e))?; + Ok(Self::from_contents(file_path.clone(), &contents)) + } + + /// Returns true if at the end of the file. + pub fn is_eof(&self) -> bool { + self.current >= self.buffer.len() + } + + /// Returns true if at the end of the line or end of the file. + pub fn is_eol(&self) -> bool { + self.is_eof() || self.peek_char() == '\n' + } + + /// Formats a location string consisting of the filename and current line number. + /// Returns a string like "MyFile.txt:33". + pub fn get_location(&self) -> String { + format!( + "{}:{}", + self.file_path.to_str().unwrap_or(""), + self.line_number + ) + } + + /// Gets the character at the current position. + /// Returns null character at end of file. + pub fn peek_char(&self) -> char { + if self.is_eof() { + '\0' + } else { + self.buffer[self.current..].chars().next().unwrap_or('\0') + } + } + + /// Gets the remainder of the current line as a string, excluding the newline character. + pub fn peek_remainder_of_line(&self) -> String { + if self.is_eof() { + return String::new(); + } + + let remaining = &self.buffer[self.current..]; + let line_end = remaining + .find('\n') + .map(|pos| self.current + pos) + .unwrap_or(self.buffer.len()); + + self.buffer[self.current..line_end].to_string() + } + + /// Gets the character at the current position and advances past it. + /// Returns null character at end of file. + pub fn next_char(&mut self) -> char { + if self.is_eof() { + return '\0'; + } + + let ch = self.peek_char(); + if ch == '\n' { + self.line_number += 1; + } + self.current += ch.len_utf8(); + ch + } + + /// Gets the next token on this line stopping at the specified delimiters + /// or the end of the line whichever comes first. + /// Returns the token or an empty string if the current character is a delimiter + /// or is at the end of the line. + pub fn next_token(&mut self, delimiters: &str) -> String { + self.skip_delimiters(delimiters); + + if self.is_eol() { + return String::new(); + } + + let start = self.current; + while !self.is_eol() && !is_delimiter(self.peek_char(), delimiters) { + self.next_char(); + } + + self.buffer[start..self.current].to_string() + } + + /// Advances to the next line. + /// Does nothing if already at the end of the file. + pub fn next_line(&mut self) { + while !self.is_eof() && self.peek_char() != '\n' { + self.next_char(); + } + if !self.is_eof() { + self.next_char(); // consume the newline + } + } + + /// Skips over the specified delimiters in the line. + pub fn skip_delimiters(&mut self, delimiters: &str) { + while !self.is_eol() && is_delimiter(self.peek_char(), delimiters) { + self.next_char(); + } + } +} + +fn is_delimiter(ch: char, delimiters: &str) -> bool { + delimiters.contains(ch) +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs b/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs new file mode 100644 index 0000000000..44bf1d8111 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/android/mod.rs @@ -0,0 +1,2 @@ +pub mod android_codes; +pub mod keylayout; diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_device_info.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_device_info.rs new file mode 100644 index 0000000000..f11eb31135 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_device_info.rs @@ -0,0 +1,8 @@ +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct EvdevDeviceInfo { + pub name: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + pub version: u16, +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs new file mode 100644 index 0000000000..365a80347a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_devices_watcher.rs @@ -0,0 +1,126 @@ +use std::path::{Path, PathBuf}; +use std::sync::{ + atomic::{AtomicBool, Ordering}, + mpsc, Arc, Mutex, RwLock, +}; + +use notify::{EventKind, RecommendedWatcher, Watcher}; +use tokio::task::JoinHandle; + +use crate::evdev_error::{EvdevError, EvdevErrorCode}; +use crate::runtime::get_runtime; + +/// Callback for when inotify events occur +pub trait InotifyCallback: Send + Sync { + fn on_inotify_dev_input(&self, paths: &[PathBuf]); +} + +pub struct EvdevDevicesWatcher { + watcher: Arc>>, + inotify_handle: RwLock>>, + enabled: Arc, +} + +impl EvdevDevicesWatcher { + pub fn new() -> Self { + Self { + watcher: Arc::new(Mutex::new(None)), + inotify_handle: RwLock::new(None), + enabled: Arc::new(AtomicBool::new(true)), + } + } + + /// Start the thread to watch /dev/input for device changes + pub fn start(&self, callback: Arc) -> Result<(), EvdevError> { + let is_running = { self.inotify_handle.read().unwrap().is_some() }; + + if is_running { + info!("Inotify watcher is already running"); + return Ok(()); + } + + // Create the channel and watcher + let (tx, rx) = mpsc::channel::>(); + + let mut watcher = notify::recommended_watcher(tx).map_err(|e| { + error!("Failed to create inotify watcher: {}", e); + EvdevError::from_enum(EvdevErrorCode::IoError) + })?; + + watcher + .watch(Path::new("/dev/input"), notify::RecursiveMode::Recursive) + .map_err(|e| { + error!("Failed to watch /dev/input: {}", e); + EvdevError::from_enum(EvdevErrorCode::IoError) + })?; + + // Store the watcher in Arc + { + let mut watcher_guard = self.watcher.lock().unwrap(); + *watcher_guard = Some(watcher); + } + + // Start the event processing loop + let callback_clone = callback.clone(); + let enabled_clone = self.enabled.clone(); + + let handle = get_runtime().spawn(async move { + for event_result in rx { + // Skip processing if disabled + if !enabled_clone.load(Ordering::Relaxed) { + continue; + } + + match event_result { + Ok(event) => { + if event.kind == EventKind::Create(notify::event::CreateKind::File) + || event.kind == EventKind::Remove(notify::event::RemoveKind::File) + { + callback_clone.on_inotify_dev_input(&event.paths); + } + } + Err(err) => { + error!("Failed to receive inotify event: {}", err); + } + } + } + }); + + self.inotify_handle.write().unwrap().replace(handle); + + Ok(()) + } + + /// Stop the thread watching /dev/input for device changes + pub fn stop(&self) -> Result<(), EvdevError> { + self.enabled.store(false, Ordering::Relaxed); + + let handle_option = self.inotify_handle.write().unwrap().take(); + + if let Some(handle) = handle_option { + handle.abort(); + } + + // Clear the watcher + let mut watcher_guard = self.watcher.lock().unwrap(); + *watcher_guard = None; + + Ok(()) + } + + /// Enable processing of inotify events + pub fn enable(&self) { + self.enabled.store(true, Ordering::Relaxed); + } + + /// Disable processing of inotify events (temporarily skip events) + pub fn disable(&self) { + self.enabled.store(false, Ordering::Relaxed); + } +} + +impl Default for EvdevDevicesWatcher { + fn default() -> Self { + Self::new() + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs new file mode 100644 index 0000000000..49a6028708 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_error.rs @@ -0,0 +1,123 @@ +/// Error type for evdev operations +#[derive(Debug, Clone, Copy, PartialEq, Eq)] +pub enum EvdevErrorCode { + NoSuchFileOrDirectory, + IoError, + NoSuchDevice, + BadFileDescriptor, + OutOfMemory, + WouldBlock, + PermissionDenied, + InvalidArgument, + Unknown(i32), +} + +impl EvdevErrorCode { + pub fn from_code(code: i32) -> Self { + match -code { + libc::ENOENT => Self::NoSuchFileOrDirectory, + libc::EIO => Self::IoError, + libc::EBADF => Self::BadFileDescriptor, + libc::EAGAIN => Self::WouldBlock, + libc::ENOMEM => Self::OutOfMemory, + libc::EACCES => Self::PermissionDenied, + libc::ENODEV => Self::NoSuchDevice, + libc::EINVAL => Self::InvalidArgument, + _ => Self::Unknown(code), + } + } + + pub fn to_code(self) -> i32 { + -(match self { + Self::NoSuchFileOrDirectory => libc::ENOENT, + Self::IoError => libc::EIO, + Self::BadFileDescriptor => libc::EBADF, + Self::WouldBlock => libc::EAGAIN, + Self::OutOfMemory => libc::ENOMEM, + Self::PermissionDenied => libc::EACCES, + Self::NoSuchDevice => libc::ENODEV, + Self::InvalidArgument => libc::EINVAL, + Self::Unknown(code) => return code, + }) + } + + pub fn description(&self) -> &'static str { + match self { + Self::NoSuchFileOrDirectory => "No such file or directory (device not found)", + Self::IoError => "Input/output error", + Self::NoSuchDevice => "No such device", + Self::BadFileDescriptor => "Bad file descriptor", + Self::OutOfMemory => "Out of memory", + Self::WouldBlock => "Resource temporarily unavailable", + Self::PermissionDenied => "Permission denied", + Self::InvalidArgument => "Invalid argument", + Self::Unknown(_) => "Unknown error", + } + } +} + +#[derive(Debug)] +pub struct EvdevError { + kind: EvdevErrorCode, + code: i32, + message: String, +} + +impl EvdevError { + pub fn new(code: i32) -> Self { + let kind = EvdevErrorCode::from_code(code); + let message = if let EvdevErrorCode::Unknown(_) = kind { + format!("evdev error: {}", code) + } else { + format!("evdev error: {} ({})", kind.description(), -code) + }; + + Self { + kind, + code, + message, + } + } + pub fn from_enum(error_code: EvdevErrorCode) -> Self { + let message = format!( + "evdev error: {} ({})", + error_code.description(), + -error_code.to_code() + ); + + Self { + kind: error_code, + code: error_code.to_code(), + message, + } + } + + pub fn code(&self) -> i32 { + self.code + } + + pub fn kind(&self) -> EvdevErrorCode { + self.kind + } +} + +impl std::fmt::Display for EvdevError { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.message) + } +} + +impl std::error::Error for EvdevError {} + +impl From for std::io::Error { + fn from(err: EvdevError) -> Self { + std::io::Error::from_raw_os_error(-err.code) + } +} + +impl From for EvdevError { + fn from(err: std::io::Error) -> Self { + let code = err.raw_os_error().unwrap_or(-1); + EvdevError::new(-code) + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs new file mode 100644 index 0000000000..848b0d0f9c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/evdev_grab_controller.rs @@ -0,0 +1,366 @@ +use std::{ + error::Error, + fs::read_dir, + io, + os::fd::AsRawFd, + path::PathBuf, + sync::{Arc, Mutex, RwLock}, +}; + +use bimap::BiHashMap; +use evdev::{enums::EventCode, DeviceWrapper}; +use mio::{unix::SourceFd, Interest, Registry, Token}; +use slab::Slab; + +use crate::{ + evdev_device_info::EvdevDeviceInfo, + evdev_devices_watcher::{EvdevDevicesWatcher, InotifyCallback}, + evdev_error::EvdevError, + event_loop::EvdevCallback, + grab_target::GrabTarget, + grabbed_device::GrabbedDevice, + grabbed_device_handle::GrabbedDeviceHandle, +}; + +pub struct EvdevGrabController { + poll_registry: Arc, + callback: Arc, + grab_targets: Mutex>, + grabbed_devices: RwLock>, + devices_watcher: EvdevDevicesWatcher, +} + +impl EvdevGrabController { + pub fn new(poll_registry: Arc, callback: Arc) -> Self { + Self { + poll_registry, + callback, + grab_targets: Mutex::new(Vec::with_capacity(64)), + grabbed_devices: RwLock::new(Slab::with_capacity(64)), + devices_watcher: EvdevDevicesWatcher::new(), + } + } + + pub fn set_grab_targets(&self, targets: Vec) -> Vec { + info!("Setting grab targets: {:?}", targets); + + let mut grab_targets = self.grab_targets.lock().unwrap(); + + grab_targets.clear(); + + for target in targets { + grab_targets.push(target); + } + + let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let handles = self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); + + handles + } + + fn invalidate( + &self, + grab_targets: &[GrabTarget], + grabbed_devices: &mut Slab, + ) -> Vec { + // Disable inotify event processing during invalidate to avoid race conditions + self.devices_watcher.disable(); + + let real_device_paths = + Self::get_real_device_paths(grabbed_devices).expect("Unable to evdev device paths"); + let device_info_path_map = Self::build_device_info_path_map(&real_device_paths); + + let device_keys_to_ungrab = Self::get_devices_to_ungrab( + grab_targets, + grabbed_devices, + device_info_path_map.clone(), + ); + + // Ungrab devices that are no longer requested + for key in device_keys_to_ungrab { + let grabbed_device = grabbed_devices.remove(key); + self.ungrab_device(grabbed_device); + } + + let devices_to_grab = + Self::get_targets_to_grab(grab_targets, grabbed_devices, device_info_path_map); + + for (path, extra_event_codes) in devices_to_grab { + self.try_grab_target(&path, &extra_event_codes, grabbed_devices) + .inspect_err(|err| error!("Failed to grab device {:?}: {:?}", path, err)) + .ok(); + } + + let grabbed_device_handles: Vec = grabbed_devices + .iter() + .map(|(key, device)| GrabbedDeviceHandle::new(key, device.device_info.clone())) + .collect(); + + info!("Grabbed devices: {:?}", grabbed_device_handles); + + self.callback + .on_grabbed_devices_changed(grabbed_device_handles.clone()); + + // Re-enable inotify event processing after invalidate is complete + self.devices_watcher.enable(); + + grabbed_device_handles + } + + /// Access a grabbed device by ID through a closure. + /// Returns None if the device is not found, otherwise returns the result of the closure. + pub fn with_grabbed_device(&self, device_id: usize, f: F) -> Option + where + F: FnOnce(&GrabbedDevice) -> R, + { + let grabbed_devices = self.grabbed_devices.read().unwrap(); + grabbed_devices.get(device_id).map(f) + } + + /// Get devices that should be ungrabed based on current grab targets and device state. + /// This function is public for testing purposes. + pub fn get_devices_to_ungrab( + grab_targets: &[GrabTarget], + grabbed_devices: &Slab, + device_info_path_map: BiHashMap, + ) -> Vec { + let mut keys_to_remove: Vec = Vec::new(); + + for (key, grabbed_device) in grabbed_devices.iter() { + let is_connected: bool = + device_info_path_map.contains_left(&grabbed_device.device_info); + + if !is_connected { + keys_to_remove.push(key); + continue; + } + + let matching_grab_target: Option<&GrabTarget> = grab_targets + .iter() + .find(|target| target.matches_device_info(&grabbed_device.device_info)); + + match matching_grab_target { + // Ungrab if the device should be grabbed with different event codes. + Some(target) => { + if target.extra_event_codes != grabbed_device.extra_event_codes { + keys_to_remove.push(key); + continue; + } + } + + // Ungrab the device if it is no longer targeted to grab + None => { + keys_to_remove.push(key); + continue; + } + } + + let current_device_at_path: Option<&EvdevDeviceInfo> = + device_info_path_map.get_by_right(&grabbed_device.device_path); + + match current_device_at_path { + // If the device path cached in the grabbed device no longer points + // to the same device + Some(real_device_info) => { + if grabbed_device.device_info != *real_device_info { + keys_to_remove.push(key); + continue; + } + } + + // The path of the grabbed device no longer exists + None => { + keys_to_remove.push(key); + continue; + } + } + } + + keys_to_remove + } + + fn ungrab_device(&self, device: GrabbedDevice) { + let fd = device.evdev.lock().unwrap().as_raw_fd(); + + let mut source_fd = SourceFd(&fd); + self.poll_registry + .deregister(&mut source_fd) + .inspect_err(|e| { + error!( + "Failed to deregister device {:?}: {:?}", + device.device_path, e + ) + }) + .ok(); + } + + /// Get targets that should be grabbed based on current grab targets and device state. + /// This function is public for testing purposes. + pub fn get_targets_to_grab( + grab_targets: &[GrabTarget], + grabbed_devices: &Slab, + device_info_path_map: BiHashMap, + ) -> Vec<(PathBuf, Vec)> { + let mut targets_to_grab: Vec<(PathBuf, Vec)> = Vec::new(); + + for target in grab_targets { + let already_grabbed = grabbed_devices + .iter() + .any(|(_, device)| target.matches_device_info(&device.device_info)); + + if already_grabbed { + continue; + } + + let device_info = device_info_path_map + .left_values() + .find(|device_info| target.matches_device_info(device_info)); + + match device_info { + // Target device not connected + None => continue, + // Device is connected + Some(device_info) => { + let path = device_info_path_map.get_by_left(device_info).unwrap(); + + targets_to_grab.push((path.clone(), target.extra_event_codes.clone())); + } + } + } + + targets_to_grab + } + + fn try_grab_target( + &self, + device_path: &PathBuf, + extra_event_codes: &[EventCode], + grabbed_devices: &mut Slab, + ) -> Result> { + let device = GrabbedDevice::new(device_path, extra_event_codes)?; + let fd = device.evdev.lock().unwrap().as_raw_fd(); + let key = grabbed_devices.insert(device); + + let mut source_fd = SourceFd(&fd); + + self.poll_registry + .register(&mut source_fd, Token(key), Interest::READABLE) + .inspect_err(|e| { + // Remove device on registration failure + grabbed_devices.remove(key); + error!("Failed to register device {:?}: {}", device_path, e); + })?; + + Ok(key) + } + + pub fn get_real_devices(&self) -> Result, EvdevError> { + let grabbed_devices = self.grabbed_devices.read().unwrap(); + + let mut list: Vec = Vec::new(); + + for path in Self::get_real_device_paths(&grabbed_devices)? { + if let Ok(info) = Self::get_device_info(&path) { + list.push(info); + } + } + + Ok(list) + } + + /// Get the paths to all the real (non uinput) connected devices. + fn get_real_device_paths( + grabbed_devices: &Slab, + ) -> Result, EvdevError> { + let uinput_paths: Vec = grabbed_devices + .iter() + .map(|(_, device)| device.uinput.devnode().unwrap().into()) + .collect(); + + let mut paths: Vec = Vec::new(); + + let dir = read_dir("/dev/input")?; + + for entry_result in dir { + match entry_result { + Ok(entry) => { + let path = entry.path(); + // Do not return paths to uinput devices that were created. + if uinput_paths.contains(&path) { + continue; + } + + paths.push(path); + } + Err(_) => { + debug!( + "Failed to read /dev/input entry: {}", + entry_result.unwrap_err() + ); + } + } + } + + Ok(paths) + } + + fn build_device_info_path_map(paths: &[PathBuf]) -> BiHashMap { + let mut map: BiHashMap = BiHashMap::new(); + + for path in paths { + if let Ok(info) = Self::get_device_info(path) { + map.insert(info, path.clone()); + } + } + + map + } + + fn get_device_info(path: &PathBuf) -> Result { + evdev::Device::new_from_path(path).map(|device| EvdevDeviceInfo { + name: device.name().unwrap_or("").to_string(), + bus: device.bustype(), + vendor: device.vendor_id(), + product: device.product_id(), + version: device.version(), + }) + } + + /// Start watching /dev/input for device changes + pub fn start_watching(self: &Arc) -> Result<(), EvdevError> { + self.devices_watcher.start(self.clone()) + } + + /// Stop watching /dev/input for device changes + pub fn stop_watching(&self) -> Result<(), EvdevError> { + self.devices_watcher.stop() + } +} + +impl InotifyCallback for EvdevGrabController { + fn on_inotify_dev_input(&self, paths: &[PathBuf]) { + let mut grabbed_devices = self.grabbed_devices.write().unwrap(); + let is_uinput_device = grabbed_devices + .iter() + .any(|(_, device)| paths.contains(&device.uinput.devnode().unwrap().into())); + + if is_uinput_device { + return; + } + + info!("inotify /dev/input event received"); + let grab_targets = self.grab_targets.lock().unwrap(); + self.invalidate(grab_targets.as_ref(), &mut grabbed_devices); + + // Notify callback about device list changes + drop(grabbed_devices); // Release the write lock before calling get_real_devices + match self.get_real_devices() { + Ok(devices) => { + self.callback.on_evdev_devices_changed(devices); + } + Err(e) => { + error!("Failed to get real devices for callback: {:?}", e); + } + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs new file mode 100644 index 0000000000..ef374aaa5a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/event_loop.rs @@ -0,0 +1,411 @@ +use crate::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; +use crate::evdev_device_info::EvdevDeviceInfo; +use crate::evdev_error::{EvdevError, EvdevErrorCode}; +use crate::evdev_grab_controller::EvdevGrabController; +use crate::grab_target::GrabTarget; +use crate::grab_target_key_code::GrabTargetKeyCode; +use crate::grabbed_device::GrabbedDevice; +use crate::grabbed_device_handle::GrabbedDeviceHandle; +use crate::runtime::get_runtime; +use evdev::enums::{EventType, EV_SYN}; +use evdev::util::event_code_to_int; +use evdev::{InputEvent, ReadFlag, ReadStatus}; +use libc::c_uint; +use log::Level; +use mio::event::Event; +use mio::{Events, Poll, Token, Waker}; +use std::error::Error; +use std::io; +use std::io::ErrorKind; +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::{Arc, OnceLock, RwLock}; +use std::time::{Duration, Instant}; +use std::{fmt, usize}; +use tokio::task::JoinHandle; + +/// Callback interface for evdev events and device changes +pub trait EvdevCallback: Send + Sync { + /// Called when an input event is received from a grabbed device. + /// Returns true if the callback consumed the event, false to pass through. + /// Parameters: device_id (slab key), device_identifier, event + fn on_evdev_event( + &self, + device_id: usize, + device_identifier: &EvdevDeviceInfo, + event: &InputEvent, + ) -> bool; + + /// Called when the list of grabbed devices changes. + /// Parameters: grabbed_devices list with their assigned IDs + fn on_grabbed_devices_changed(&self, grabbed_devices: Vec); + + /// Called when the list of available evdev devices changes. + /// Parameters: devices list of all available evdev devices + fn on_evdev_devices_changed(&self, devices: Vec); +} + +static EVENT_LOOP_MANAGER: OnceLock = OnceLock::new(); + +const WAKER_TOKEN: Token = Token(usize::MAX - 1); + +pub struct EventLoopManager { + stop_flag: Arc, + poll: Arc>, + event_loop_handle: RwLock>>, + waker: Waker, + callback: Arc, + grab_controller: Arc, +} + +impl fmt::Debug for EventLoopManager { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + f.debug_struct("EventLoopManager") + .field("stop_flag", &self.stop_flag.load(Ordering::SeqCst)) + .field("poll", &"") + .field("registry", &"") + .field("join_handle", &"") + .field("waker", &"") + .field("callback", &"") + .field("grab_controller", &"") + .finish() + } +} + +impl EventLoopManager { + /// Initialize the EventLoopManager with a callback. Must be called once before `get()`. + /// Panics if called more than once. + pub fn init(callback: Arc) { + EVENT_LOOP_MANAGER + .set(Self::new(callback)) + .expect("EventLoopManager already initialized"); + } + + /// Get the EventLoopManager instance. Panics if `init()` was not called. + pub fn get() -> &'static EventLoopManager { + EVENT_LOOP_MANAGER + .get() + .expect("EventLoopManager not initialized. Call init() first.") + } + + fn new(callback: Arc) -> Self { + let poll = Poll::new().unwrap(); + let registry = poll.registry().try_clone().unwrap(); + let waker = Waker::new(®istry, WAKER_TOKEN).unwrap(); + let poll_lock = Arc::new(RwLock::new(poll)); + + let registry_arc = Arc::new(registry); + let grab_controller = EvdevGrabController::new(registry_arc.clone(), callback.clone()); + + Self { + stop_flag: Arc::new(AtomicBool::new(false)), + poll: poll_lock, + event_loop_handle: RwLock::new(None), + waker, + callback, + grab_controller: Arc::new(grab_controller), + } + } + + pub fn start(&self) -> Result<(), EvdevError> { + let is_running = { self.event_loop_handle.read().unwrap().is_some() }; + + if is_running { + info!("EvdevManager event loop is already running"); + return Ok(()); + } + + self.stop_flag.store(false, Ordering::Relaxed); + + let callback = self.callback.clone(); + + let poll_lock_clone = self.poll.clone(); + let stop_flag_clone = self.stop_flag.clone(); + let grab_controller_event_loop = self.grab_controller.clone(); + + let event_loop_handle = get_runtime().spawn(async move { + EventLoopThread::new( + stop_flag_clone, + poll_lock_clone, + callback, + grab_controller_event_loop, + ) + .start(); + }); + + self.event_loop_handle + .write() + .unwrap() + .replace(event_loop_handle); + + self.grab_controller + .start_watching() + .inspect_err(|err| error!("Failed to start inotify watching: {:?}", err))?; + + Ok(()) + } + + pub fn stop(&self) -> Result<(), io::Error> { + // Stop inotify watching + self.grab_controller + .stop_watching() + .inspect_err(|err| error!("Failed to stop inotify watching: {:?}", err)) + .ok(); + + let handle_option = self.event_loop_handle.write().unwrap().take(); + + match handle_option { + None => { + error!("Event loop not running"); + } + Some(handle) => { + self.stop_flag.store(true, Ordering::Relaxed); + self.waker.wake()?; + + // Wait for the loop to finish (with timeout) + let start = Instant::now(); + while !handle.is_finished() { + if start.elapsed() > Duration::from_secs(2) { + error!("Event loop did not stop in time, aborting"); + handle.abort(); + + return Err(io::Error::new( + ErrorKind::TimedOut, + "Event loop did not stop in time", + )); + } + std::thread::sleep(Duration::from_millis(10)); + } + } + } + + Ok(()) + } + + /// Set the list of grabbed devices. This will ungrab any devices that are no longer in the list + /// and grab any new devices. Devices are matched by DeviceIdentifier (name, bus, vendor, product). + /// Returns: A list of (device_id, DeviceIdentifier) for all successfully grabbed devices. + pub fn set_grab_targets(&self, targets: Vec) -> Vec { + let internal_grab_targets = targets.iter().map(Self::convert_grab_target).collect(); + + let handles = self.grab_controller.set_grab_targets(internal_grab_targets); + + for handle in handles.clone() { + KeyLayoutMapManager::get() + .preload_key_layout_map(&handle.device_info) + .inspect_err(|err| { + error!( + "Failed to preload key layout map for device {:?}: {}", + handle.device_info, err + ); + }) + .ok(); + } + + handles + } + + pub fn get_real_devices(&self) -> Result, EvdevError> { + self.grab_controller.get_real_devices() + } + + /// Write an event to a grabbed device's uinput. + /// The device_id is the slab key returned by set_grabbed_devices(), enabling O(1) lookup. + pub fn write_event( + &self, + device_id: usize, + event_type: u32, + code: u32, + value: i32, + ) -> Result<(), EvdevError> { + if log_enabled!(Level::Debug) { + debug!( + "Write evdev event: device_id={} event_type={} code={} value={}", + device_id, event_type, code, value + ); + } + + self.grab_controller + .with_grabbed_device(device_id, |device| { + device + .uinput + .write_event(event_type, code, value) + .map_err(EvdevError::from) + }) + .ok_or(EvdevError::from_enum(EvdevErrorCode::NoSuchDevice))? + } + + pub fn write_key_code_event( + &self, + device_id: usize, + key_code: u32, + value: i32, + ) -> Result<(), Box> { + let result = self + .grab_controller + .with_grabbed_device(device_id, |device| { + let scan_code_result = KeyLayoutMapManager::get() + .find_scan_code_for_key(&device.device_info, key_code); + + match scan_code_result { + Err(e) => Err(e.into()), + Ok(None) => { + error!("Failed to find scan code for key: {}", key_code); + Err(Box::new(EvdevError::new(-libc::ENODATA)) as Box) + } + Ok(Some(code)) => { + if log_enabled!(Level::Debug) { + debug!( + "Write key code evdev event: key_code={} value={}", + key_code, value + ); + } + + device + .uinput + .write_event(EventType::EV_KEY as c_uint, code, value) + .map_err(|err| Box::new(err) as Box)?; + + // Send SYN_REPORT + device + .uinput + .write_event( + EventType::EV_SYN as c_uint, + EV_SYN::SYN_REPORT as c_uint, + 0, + ) + .map_err(|err| Box::new(err) as Box) + } + } + }); + + match result { + Some(inner_result) => inner_result, + None => Err(Box::new(EvdevError::from_enum( + EvdevErrorCode::NoSuchDevice, + ))), + } + } + + fn convert_grab_target(target: &GrabTargetKeyCode) -> GrabTarget { + let event_codes = + KeyLayoutMapManager::map_key_codes_to_event_codes(&target.extra_key_codes); + + GrabTarget { + name: target.name.clone(), + bus: target.bus, + vendor: target.vendor, + product: target.product, + extra_event_codes: event_codes, + } + } +} + +struct EventLoopThread { + stop_flag: Arc, + poll: Arc>, + callback: Arc, + grab_controller: Arc, +} + +impl EventLoopThread { + pub fn new( + stop_flag: Arc, + poll: Arc>, + callback: Arc, + grab_controller: Arc, + ) -> Self { + EventLoopThread { + stop_flag, + poll, + callback, + grab_controller, + } + } + + /// This blocks until the stop flag is set. + fn start(&self) { + let mut events = Events::with_capacity(128); + + info!("Started evdev event loop"); + + 'main: loop { + let mut poll = self.poll.write().unwrap(); + + match poll.poll(&mut events, None) { + Ok(_) => { + for event in events.iter() { + // Break out of the loop if the stop flag is set. + if event.token() == WAKER_TOKEN && self.stop_flag.load(Ordering::SeqCst) { + info!("Received waker token. Stopping evdev event loop"); + break 'main; + } + + self.on_poll_event(event); + } + } + Err(e) if e.kind() == ErrorKind::Interrupted => { + // Interrupted, continue polling + continue; + } + Err(e) => { + error!("EvdevManager poll error. Stopping loop: {}", e); + break; + } + } + } + + info!("Stopped evdev event loop"); + } + + fn on_poll_event(&self, event: &Event) { + let Token(key) = event.token(); + let slab_key = key; + + self.grab_controller + .with_grabbed_device(slab_key, |device| { + let evdev = device.evdev.lock().unwrap(); + let mut flags: ReadFlag = ReadFlag::NORMAL; + + loop { + match evdev.next_event(flags) { + Ok((ReadStatus::Success, input_event)) => { + flags = ReadFlag::NORMAL; + if log_enabled!(Level::Debug) { + debug!("Evdev event: {:?}", input_event); + } + self.process_event(slab_key, &input_event, device); + } + Ok((ReadStatus::Sync, _event)) => { + // Continue reading sync events + flags = ReadFlag::NORMAL | ReadFlag::SYNC; + } + Err(_error) => { + // Break if it's EAGAIN (no more events) or any other error. + // Do not log these errors because it is expected + break; + } + } + } + }); + } + + fn process_event(&self, device_id: usize, event: &InputEvent, grabbed_device: &GrabbedDevice) { + let consumed = self + .callback + .on_evdev_event(device_id, &grabbed_device.device_info, event); + + if !consumed { + let (event_type, event_code) = event_code_to_int(&event.event_code); + grabbed_device + .uinput + .write_event(event_type, event_code, event.value) + .inspect_err(|e| { + error!( + "Failed to passthrough event to {:?}. Event: {:?}. Error: {:?}", + grabbed_device.device_path, event, e + ) + }) + .ok(); + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grab_target.rs b/evdev/src/main/rust/evdev_manager/core/src/grab_target.rs new file mode 100644 index 0000000000..248569b18a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grab_target.rs @@ -0,0 +1,25 @@ +use evdev::enums::EventCode; + +use crate::evdev_device_info::EvdevDeviceInfo; + +/// The information required to grab a device. +/// +#[derive(Hash, Eq, PartialEq, Clone, Debug)] +pub struct GrabTarget { + pub name: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + /// The extra event codes that should be enabled for the device. This is so that the + /// uinput device can input events that the original device didn't support. + pub extra_event_codes: Vec, +} + +impl GrabTarget { + pub fn matches_device_info(&self, device_info: &EvdevDeviceInfo) -> bool { + device_info.name == self.name + && device_info.bus == self.bus + && device_info.vendor == self.vendor + && device_info.product == self.product + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs b/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs new file mode 100644 index 0000000000..26d81c1358 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grab_target_key_code.rs @@ -0,0 +1,8 @@ +#[derive(Debug)] +pub struct GrabTargetKeyCode { + pub name: String, + pub bus: u16, + pub vendor: u16, + pub product: u16, + pub extra_key_codes: Vec, +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs new file mode 100644 index 0000000000..861d995705 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device.rs @@ -0,0 +1,75 @@ +use crate::evdev_device_info::EvdevDeviceInfo; +use crate::evdev_error::EvdevError; +use evdev::enums::EventCode; +use evdev::{Device, DeviceWrapper, GrabMode, UInputDevice}; +use std::fs::OpenOptions; +use std::os::unix::fs::OpenOptionsExt; +use std::path::PathBuf; +use std::sync::Mutex; + +/// Device context containing all information about a grabbed evdev device +pub struct GrabbedDevice { + pub device_path: PathBuf, + pub device_info: EvdevDeviceInfo, + /// The libevdev Device can not be shared safely across threads so wrap it in a mutex. + pub evdev: Mutex, + pub uinput: UInputDevice, + /// The extra event codes that were enabled for the uinput device. This is so that the + /// uinput device can input events that the original device didn't support. + pub extra_event_codes: Vec, +} + +impl GrabbedDevice { + /// Create a grabbed device that also enables the given EventCodes in the uinput device. + pub fn new(device_path: &PathBuf, extra_events: &[EventCode]) -> Result { + let mut evdev = Self::open_evdev_device(device_path)?; + + for event in extra_events { + evdev.enable(*event)?; + } + + evdev.grab(GrabMode::Grab).map_err(EvdevError::from)?; + let uinput = UInputDevice::create_from_device(&evdev).map_err(EvdevError::from)?; + + let device_info = EvdevDeviceInfo { + name: evdev.name().unwrap_or("").to_string(), + bus: evdev.bustype(), + vendor: evdev.vendor_id(), + product: evdev.product_id(), + version: evdev.version(), + }; + + Ok(Self { + device_path: device_path.clone(), + device_info, + evdev: Mutex::new(evdev), + uinput, + extra_event_codes: extra_events.into(), + }) + } + + fn open_evdev_device(device_path: &PathBuf) -> Result { + // Open device with O_NONBLOCK so that the loop reading events eventually returns + // due to an EAGAIN error + + let file = OpenOptions::new() + .read(true) + .custom_flags(libc::O_NONBLOCK) + .open(device_path) + .map_err(EvdevError::from)?; + + let evdev = Device::new_from_file(file).map_err(EvdevError::from)?; + Ok(evdev) + } +} + +impl Drop for GrabbedDevice { + fn drop(&mut self) { + let mut evdev = self.evdev.lock().unwrap(); + // Ungrab the device + evdev + .grab(GrabMode::Ungrab) + .inspect_err(|err| error!("Failed to ungrab device {:?}; {}", self.device_info, err)) + .ok(); + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs new file mode 100644 index 0000000000..5c1d334250 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/grabbed_device_handle.rs @@ -0,0 +1,14 @@ +use crate::evdev_device_info::EvdevDeviceInfo; + +/// Handle to a grabbed device with its assigned ID for O(1) lookup +#[derive(Debug, Clone, PartialEq, Eq)] +pub struct GrabbedDeviceHandle { + pub id: usize, + pub device_info: EvdevDeviceInfo, +} + +impl GrabbedDeviceHandle { + pub fn new(id: usize, device_info: EvdevDeviceInfo) -> Self { + Self { id, device_info } + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/src/lib.rs b/evdev/src/main/rust/evdev_manager/core/src/lib.rs new file mode 100644 index 0000000000..40b0c8b813 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/lib.rs @@ -0,0 +1,13 @@ +#[macro_use] +extern crate log; +pub mod android; +pub mod evdev_device_info; +pub mod evdev_devices_watcher; +pub mod evdev_error; +pub mod evdev_grab_controller; +pub mod event_loop; +pub mod grab_target; +pub mod grab_target_key_code; +pub mod grabbed_device; +pub mod grabbed_device_handle; +pub mod runtime; diff --git a/evdev/src/main/rust/evdev_manager/core/src/runtime.rs b/evdev/src/main/rust/evdev_manager/core/src/runtime.rs new file mode 100644 index 0000000000..ba238adcf6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/src/runtime.rs @@ -0,0 +1,14 @@ +use std::sync::OnceLock; +use tokio::runtime::Runtime; + +static RUNTIME: OnceLock = OnceLock::new(); + +pub fn get_runtime() -> &'static Runtime { + RUNTIME.get_or_init(|| { + tokio::runtime::Builder::new_multi_thread() + .worker_threads(2) // Optional: limit threads to save resources on Android + .enable_all() + .build() + .expect("Failed to create Tokio runtime") + }) +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs new file mode 100644 index 0000000000..0d4189f0f3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/input_event_lookup_test.rs @@ -0,0 +1,32 @@ +//! Integration tests for key layout map parsing. + +use evdev_manager_core::android::android_codes::{ + AKEYCODE_A, AKEYCODE_FUNCTION, AMOTION_EVENT_AXIS_X, POLICY_FLAG_VIRTUAL, +}; +use evdev_manager_core::android::keylayout::input_event_lookup; +#[cfg(test)] +use pretty_assertions::assert_eq; + +#[test] +fn test_function_key_lookup() { + let key_code = input_event_lookup::get_key_code_by_label("FUNCTION"); + assert_eq!(key_code, Some(AKEYCODE_FUNCTION)); +} + +#[test] +fn test_letter_key_lookup() { + let key_code = input_event_lookup::get_key_code_by_label("A"); + assert_eq!(key_code, Some(AKEYCODE_A)); +} + +#[test] +fn test_policy_flag_lookup() { + let code = input_event_lookup::get_key_flag_by_label("VIRTUAL"); + assert_eq!(code, Some(POLICY_FLAG_VIRTUAL)); +} + +#[test] +fn test_axis_lookup() { + let code = input_event_lookup::get_axis_by_label("X"); + assert_eq!(code, Some(AMOTION_EVENT_AXIS_X)); +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs new file mode 100644 index 0000000000..5f80f13d0a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_manager_test.rs @@ -0,0 +1,698 @@ +//! Tests for KeyLayoutMapManager file finding logic. +use assertables::{assert_iter_eq, assert_some, assert_some_eq}; +use evdev_manager_core::android::android_codes::{ + AKEYCODE_ESCAPE, AKEYCODE_HOME, AKEYCODE_MINUS, AKEYCODE_MOVE_HOME, AKEYCODE_SPACE, +}; +use evdev_manager_core::android::keylayout::key_layout_file_finder::KeyLayoutFileFinder; +use evdev_manager_core::android::keylayout::key_layout_map_manager::{ + get_generic_key_layout_map, KeyLayoutMapManager, +}; +use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; +#[cfg(test)] +use pretty_assertions::assert_eq; +use std::collections::HashMap; +use std::path::PathBuf; +use std::sync::Arc; + +/// Mock file finder that returns predefined paths for testing. +struct MockFileFinder { + system_files: HashMap, + key_mapper_files: HashMap, +} + +impl MockFileFinder { + fn new() -> Self { + Self { + system_files: HashMap::new(), + key_mapper_files: HashMap::new(), + } + } + + fn add_system_file(mut self, name: &str, path: PathBuf) -> Self { + self.system_files.insert(name.to_string(), path); + self + } +} + +impl KeyLayoutFileFinder for MockFileFinder { + fn find_system_key_layout_file_by_name(&self, name: &str) -> Option { + self.system_files.get(name).cloned() + } + + fn find_key_mapper_key_layout_file_by_name(&self, name: &str) -> Option { + self.key_mapper_files.get(name).cloned() + } +} + +fn find_key_layout_files_str( + mock_file_finder: Arc, + device: &EvdevDeviceInfo, +) -> Vec { + let manager = KeyLayoutMapManager::with_file_finder(mock_file_finder); + + manager + .find_key_layout_files(&device) + .iter() + .map(|path| path.to_str().unwrap().to_string()) + .collect() +} + +fn get_test_data_path() -> PathBuf { + let test_data_dir = env!("CARGO_MANIFEST_DIR"); + PathBuf::from(test_data_dir).join("tests").join("test_data") +} + +#[test] +fn test_find_no_files() { + let mock_finder = Arc::new(MockFileFinder::new()); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x1234, + product: 0x5678, + version: 0x0001, + }; + + let files: Vec = find_key_layout_files_str(mock_finder, &device); + + assert_eq!(files.len(), 0) +} + +#[test] +fn test_only_find_generic_file() { + let mock_finder = Arc::new( + MockFileFinder::new() + .add_system_file("Generic", PathBuf::from("/system/usr/keylayout/Generic.kl")), + ); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x1234, + product: 0x5678, + version: 0x0001, + }; + + let files: Vec = find_key_layout_files_str(mock_finder, &device); + + let expected = ["/system/usr/keylayout/Generic.kl"]; + + assert_iter_eq!(files, expected); +} + +#[test] +fn test_find_key_layout_files_priority_order() { + let mock_finder = Arc::new( + MockFileFinder::new() + .add_system_file( + "Vendor_1234_Product_5678_Version_0001", + PathBuf::from("/system/usr/keylayout/Vendor_1234_Product_5678_Version_0001.kl"), + ) + .add_system_file( + "Vendor_1234_Product_5678", + PathBuf::from("/system/usr/keylayout/Vendor_1234_Product_5678.kl"), + ) + .add_system_file( + "gpio-keys", + PathBuf::from("/system/usr/keylayout/gpio-keys.kl"), + ) + .add_system_file("Generic", PathBuf::from("/system/usr/keylayout/Generic.kl")), + ); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x1234, + product: 0x5678, + version: 0x0001, + }; + + let files: Vec = find_key_layout_files_str(mock_finder, &device); + + let expected = [ + "/system/usr/keylayout/Vendor_1234_Product_5678_Version_0001.kl", + "/system/usr/keylayout/Vendor_1234_Product_5678.kl", + "/system/usr/keylayout/gpio-keys.kl", + "/system/usr/keylayout/Generic.kl", + ]; + + assert_iter_eq!(files, expected); +} + +#[test] +fn test_preload_key_layout_map_returns_cached_result() { + // Create a mock file finder that returns a path to a valid key layout file + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x0000, + product: 0x0000, + version: 0x0000, + }; + + let result1 = manager.preload_key_layout_map(&device).unwrap(); + // Verify the cached value is returned by comparing the pointers + let result2 = manager.preload_key_layout_map(&device).unwrap(); + + assert!( + Arc::ptr_eq(&result1.unwrap(), &result2.unwrap()), + "Second call should return the same cached Arc instance" + ); +} + +#[test] +fn test_preload_key_layout_map_saves_some_when_found() { + // Create a mock file finder that returns no files + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + assert_some!(manager.preload_key_layout_map(&device).unwrap()); + let cache = manager.key_layout_maps.lock().unwrap(); + assert!(cache.contains_key(&device)); + assert_some!(cache.get(&device).unwrap()); +} + +#[test] +fn test_preload_key_layout_map_uses_fallback_when_not_found() { + // Create a mock file finder that returns no files + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Should return the hardcoded fallback, not None + assert_some!(manager.preload_key_layout_map(&device).unwrap()); + let cache = manager.key_layout_maps.lock().unwrap(); + assert!(cache.contains_key(&device)); + assert_some!(cache.get(&device).unwrap()); +} + +#[test] +fn test_map_key_reads_from_cache() { + // Create a mock file finder that returns no files + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + let cached_key_layout_map = manager.preload_key_layout_map(&device).unwrap().unwrap(); + let cached_key = cached_key_layout_map.map_key(12).unwrap(); + + // Verify the cached value is returned + let map_key_result = manager.map_key(&device, 12).unwrap().unwrap(); + + assert_eq!(cached_key, map_key_result, "Key codes should be equal"); +} + +#[test] +fn test_map_key_saves_to_cache() { + // Create a mock file finder that returns no files + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Verify the cached value is returned + let cached_key = manager.map_key(&device, 12).unwrap().unwrap(); + let map_key = manager.map_key(&device, 12).unwrap().unwrap(); + + assert_eq!(cached_key, map_key, "Key codes should be equal"); +} + +#[test] +fn test_map_key_finds_file_if_cache_miss() { + // Create a mock file finder that returns no files + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Verify the key is found and has the correct key code + let map_key_result = manager.map_key(&device, 12).unwrap().unwrap(); + + assert_eq!(map_key_result, AKEYCODE_MINUS); +} + +#[test] +fn test_map_key_reads_first_found_path() { + // Create a mock file finder that returns no files + let mock_finder = MockFileFinder::new() + .add_system_file("Generic", get_test_data_path().join("Generic.kl")) + .add_system_file("gpio-keys", get_test_data_path().join("6t/gpio-keys.kl")); + + let manager = KeyLayoutMapManager::with_file_finder(Arc::new(mock_finder)); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Verify the correct key code is returned (device-specific file takes priority) + let map_key_result = manager.map_key(&device, 102).unwrap().unwrap(); + + // In gpio-keys.kl this is HOME and in Generic.kl this is MOVE_HOME + assert_eq!(map_key_result, AKEYCODE_HOME); +} + +#[test] +fn test_map_key_reads_generic_if_device_specific_not_found() { + // Create a mock file finder that returns no files + let mock_finder = + MockFileFinder::new().add_system_file("Generic", get_test_data_path().join("Generic.kl")); + + let manager = KeyLayoutMapManager::with_file_finder(Arc::new(mock_finder)); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Verify the fallback to Generic.kl works + let map_key_result = manager.map_key(&device, 102).unwrap().unwrap(); + + // In gpio-keys.kl this is HOME and in Generic.kl this is MOVE_HOME + assert_eq!(map_key_result, AKEYCODE_MOVE_HOME); +} + +#[test] +fn test_get_generic_key_layout_map_returns_valid_map() { + let generic_map = get_generic_key_layout_map(); + + // Test some well-known key mappings from Generic.kl + assert_eq!(generic_map.map_key(1), Some(AKEYCODE_ESCAPE)); // ESCAPE + assert_eq!(generic_map.map_key(57), Some(AKEYCODE_SPACE)); // SPACE + assert_eq!(generic_map.map_key(102), Some(AKEYCODE_MOVE_HOME)); // MOVE_HOME +} + +#[test] +fn test_get_generic_key_layout_map_is_static() { + // Calling get_generic_key_layout_map multiple times should return the same instance + let map1 = get_generic_key_layout_map(); + let map2 = get_generic_key_layout_map(); + + // They should be the same Arc (same pointer) + assert!(Arc::ptr_eq(&map1, &map2)); +} + +#[test] +fn test_get_generic_key_layout_map_reverse_lookup() { + let generic_map = get_generic_key_layout_map(); + + let escape_scan_code = generic_map.find_scan_code_for_key(AKEYCODE_ESCAPE); + assert_some_eq!(escape_scan_code, Some(1)); +} + +#[test] +fn test_fallback_to_hardcoded_generic_when_no_files_found() { + // Create a mock file finder that returns no files at all + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Should still return a valid key layout map (the hardcoded fallback) + let result = manager.preload_key_layout_map(&device).unwrap(); + assert!(result.is_some(), "Should return hardcoded Generic fallback"); + + // Verify the fallback map has the expected mappings + let map = result.unwrap(); + assert_eq!(map.map_key(1), Some(AKEYCODE_ESCAPE)); // ESCAPE + assert_eq!(map.map_key(57), Some(AKEYCODE_SPACE)); // SPACE + assert_eq!(map.map_key(102), Some(AKEYCODE_MOVE_HOME)); // MOVE_HOME +} + +#[test] +fn test_fallback_map_key_works_when_no_files_found() { + // Create a mock file finder that returns no files + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // map_key should work using the hardcoded fallback + let key_code = manager.map_key(&device, 1).unwrap(); + assert_eq!(key_code, Some(AKEYCODE_ESCAPE)); + + let key_code = manager.map_key(&device, 57).unwrap(); + assert_eq!(key_code, Some(AKEYCODE_SPACE)); + + // Unknown scan code should return None + let key_code = manager.map_key(&device, 9999).unwrap(); + assert_eq!(key_code, None); +} + +#[test] +fn test_fallback_is_cached() { + // Create a mock file finder that returns no files + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // First call should use the fallback + let result1 = manager.preload_key_layout_map(&device).unwrap().unwrap(); + + // Second call should return the same cached instance + let result2 = manager.preload_key_layout_map(&device).unwrap().unwrap(); + + assert!( + Arc::ptr_eq(&result1, &result2), + "Fallback should be cached and return same Arc instance" + ); +} + +#[test] +fn test_fallback_uses_static_generic_map() { + // Create a mock file finder that returns no files + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Get the fallback via manager + let manager_map = manager.preload_key_layout_map(&device).unwrap().unwrap(); + + // Get the static generic map directly + let static_map = get_generic_key_layout_map(); + + // They should be the same Arc instance + assert!( + Arc::ptr_eq(&manager_map, &static_map), + "Manager fallback should use the static generic map" + ); +} + +#[test] +fn test_map_key_falls_back_to_generic_for_missing_scan_code() { + // gpio-keys.kl only has a few keys (like HOME on scan code 102) + // It doesn't have ESCAPE (scan code 1), so it should fall back to Generic + let mock_finder = Arc::new( + MockFileFinder::new() + .add_system_file("gpio-keys", get_test_data_path().join("6t/gpio-keys.kl")), + ); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x0000, + product: 0x0000, + version: 0x0000, + }; + + // Scan code 102 is in gpio-keys.kl and maps to HOME + let key_code = manager.map_key(&device, 102).unwrap(); + assert_eq!(key_code, Some(AKEYCODE_HOME)); + + // Scan code 1 (ESCAPE) is NOT in gpio-keys.kl, so it should fall back to Generic + let key_code = manager.map_key(&device, 1).unwrap(); + assert_eq!(key_code, Some(AKEYCODE_ESCAPE)); + + // Scan code 57 (SPACE) is NOT in gpio-keys.kl, so it should fall back to Generic + let key_code = manager.map_key(&device, 57).unwrap(); + assert_eq!(key_code, Some(AKEYCODE_SPACE)); +} + +#[test] +fn test_find_scan_code_for_key_falls_back_to_generic_for_missing_key_code() { + // gpio-keys.kl only has a few keys + // It doesn't have ESCAPE, so reverse lookup should fall back to Generic + let mock_finder = Arc::new( + MockFileFinder::new() + .add_system_file("gpio-keys", get_test_data_path().join("6t/gpio-keys.kl")), + ); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x0000, + product: 0x0000, + version: 0x0000, + }; + + // HOME is in gpio-keys.kl, should return scan code 102 + let scan_code = manager + .find_scan_code_for_key(&device, AKEYCODE_HOME) + .unwrap(); + assert_eq!(scan_code, Some(102)); + + // ESCAPE is NOT in gpio-keys.kl, should fall back to Generic (scan code 1) + let scan_code = manager + .find_scan_code_for_key(&device, AKEYCODE_ESCAPE) + .unwrap(); + assert_eq!(scan_code, Some(1)); + + // SPACE is NOT in gpio-keys.kl, should fall back to Generic (scan code 57) + let scan_code = manager + .find_scan_code_for_key(&device, AKEYCODE_SPACE) + .unwrap(); + assert_eq!(scan_code, Some(57)); +} + +#[test] +fn test_find_scan_code_for_key_reads_from_cache() { + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + let cached_key_layout_map = manager.preload_key_layout_map(&device).unwrap().unwrap(); + let cached_scan_code = cached_key_layout_map + .find_scan_code_for_key(AKEYCODE_MINUS) + .unwrap(); + + // Verify the cached value is returned + let scan_code_result = manager + .find_scan_code_for_key(&device, AKEYCODE_MINUS) + .unwrap() + .unwrap(); + + assert_eq!( + cached_scan_code, scan_code_result, + "Scan codes should be equal" + ); +} + +#[test] +fn test_find_scan_code_for_key_saves_to_cache() { + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // First call loads and caches the map + let first_result = manager + .find_scan_code_for_key(&device, AKEYCODE_MINUS) + .unwrap() + .unwrap(); + // Second call should use the cache + let second_result = manager + .find_scan_code_for_key(&device, AKEYCODE_MINUS) + .unwrap() + .unwrap(); + + assert_eq!(first_result, second_result, "Scan codes should be equal"); +} + +#[test] +fn test_find_scan_code_for_key_finds_file_if_cache_miss() { + let test_kl_path = get_test_data_path().join("Generic.kl"); + let mock_finder = + Arc::new(MockFileFinder::new().add_system_file("Generic", test_kl_path.clone())); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Test Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // MINUS key code should map to scan code 12 in Generic.kl + let scan_code_result = manager + .find_scan_code_for_key(&device, AKEYCODE_MINUS) + .unwrap() + .unwrap(); + + assert_eq!(scan_code_result, 12); +} + +#[test] +fn test_find_scan_code_for_key_reads_first_found_path() { + // gpio-keys.kl has HOME -> 102, Generic.kl has HOME -> 172 + let mock_finder = MockFileFinder::new() + .add_system_file("Generic", get_test_data_path().join("Generic.kl")) + .add_system_file("gpio-keys", get_test_data_path().join("6t/gpio-keys.kl")); + + let manager = KeyLayoutMapManager::with_file_finder(Arc::new(mock_finder)); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Device-specific file (gpio-keys.kl) should take priority + // In gpio-keys.kl, HOME maps to scan code 102 + let scan_code_result = manager + .find_scan_code_for_key(&device, AKEYCODE_HOME) + .unwrap() + .unwrap(); + + assert_eq!(scan_code_result, 102); +} + +#[test] +fn test_find_scan_code_for_key_reads_generic_if_device_not_found() { + let mock_finder = + MockFileFinder::new().add_system_file("Generic", get_test_data_path().join("Generic.kl")); + + let manager = KeyLayoutMapManager::with_file_finder(Arc::new(mock_finder)); + + let device = EvdevDeviceInfo { + name: "gpio-keys".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // No device-specific file found, fallback to Generic.kl + // In Generic.kl, MOVE_HOME maps to scan code 102 + let scan_code_result = manager + .find_scan_code_for_key(&device, AKEYCODE_MOVE_HOME) + .unwrap() + .unwrap(); + + assert_eq!(scan_code_result, 102); +} + +#[test] +fn test_find_scan_code_for_key_returns_none_for_unknown_key() { + let mock_finder = Arc::new(MockFileFinder::new()); + + let manager = KeyLayoutMapManager::with_file_finder(mock_finder); + + let device = EvdevDeviceInfo { + name: "Unknown Device".to_string(), + bus: 0x0003, + vendor: 0x9999, + product: 0x8888, + version: 0x0001, + }; + + // Unknown key code should return None + let scan_code = manager.find_scan_code_for_key(&device, 99999).unwrap(); + assert_eq!(scan_code, None); +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs new file mode 100644 index 0000000000..38e62f2471 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/key_layout_map_test.rs @@ -0,0 +1,309 @@ +//! Integration tests for key layout map parsing. +use evdev_manager_core::android::android_codes::{AKEYCODE_F4, AKEYCODE_FUNCTION}; +use evdev_manager_core::android::keylayout::key_layout_map; +use evdev_manager_core::android::keylayout::key_layout_map::{KeyLayoutAxisMode, KeyLayoutMap}; +use glob::glob; +#[cfg(test)] +use pretty_assertions::assert_eq; +use std::path::{Path, PathBuf}; + +fn get_test_data_dir() -> &'static str { + env!("CARGO_MANIFEST_DIR") +} + +fn get_test_data_path() -> String { + format!("{}/tests/test_data", get_test_data_dir()) +} + +fn load_key_layout_map(file_name: &str) -> KeyLayoutMap { + let file_path = PathBuf::new().join(get_test_data_path()).join(file_name); + KeyLayoutMap::load_from_file(file_path).unwrap() +} + +#[test] +fn test_parse_int() { + assert_eq!(key_layout_map::parse_int("123"), Some(123)); + assert_eq!(key_layout_map::parse_int("-999"), Some(-999)); + assert_eq!(key_layout_map::parse_int("0x1a"), Some(26)); + assert_eq!(key_layout_map::parse_int("0XFF"), Some(255)); + assert_eq!(key_layout_map::parse_int("077"), Some(63)); // octal + assert_eq!(key_layout_map::parse_int(""), None); + assert_eq!(key_layout_map::parse_int("abc"), None); +} + +#[test] +fn test_load_from_contents_simple() { + let contents = "key 1 ESCAPE\nkey 2 1\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let key_code = map.map_key(1).unwrap(); + assert_eq!(key_code, 111); // ESCAPE + + let key_code = map.map_key(2).unwrap(); + assert_eq!(key_code, 8); // KEYCODE_1 +} + +#[test] +fn test_load_from_contents_with_flags() { + // Flags are parsed but ignored - we just verify the key code is correct + let contents = "key 465 ESCAPE FUNCTION\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let key_code = map.map_key(465).unwrap(); + assert_eq!(key_code, 111); // ESCAPE +} + +#[test] +fn test_load_from_contents_axis() { + let contents = "axis 0x00 X\naxis 0x01 Y\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + let axis_info = map.map_axis(0x00).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 0); // X axis + + let axis_info = map.map_axis(0x01).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 1); // Y axis +} + +#[test] +fn test_function_key_line() { + let content = "key 464 FUNCTION"; + let map = KeyLayoutMap::load_from_contents(content).unwrap(); + let key_code = map.map_key(464).unwrap(); + assert_eq!(key_code, AKEYCODE_FUNCTION); +} + +#[test] +fn test_f4_key_line() { + let content = "key 469 F4 FUNCTION"; + let map = KeyLayoutMap::load_from_contents(content).unwrap(); + let key_code = map.map_key(469).unwrap(); + assert_eq!(key_code, AKEYCODE_F4); +} + +#[test] +fn test_brightness_usage_key_lines() { + let content = "\ + key usage 0x0c0067 WINDOW + key usage 0x0c006F BRIGHTNESS_UP + key usage 0x0c0070 BRIGHTNESS_DOWN"; + + KeyLayoutMap::load_from_contents(content).unwrap(); + // Just do not crash because it should be skipped. +} + +#[test] +fn test_load_generic_kl() { + let map = load_key_layout_map("Generic.kl"); + + // Test some basic key mappings + let key_code = map.map_key(1).expect("Scan code 1 should map to ESCAPE"); + assert_eq!(key_code, 111); // ESCAPE + + let key_code = map.map_key(2).expect("Scan code 2 should map to 1"); + assert_eq!(key_code, 8); // KEYCODE_1 + + let key_code = map.map_key(15).expect("Scan code 15 should map to TAB"); + assert_eq!(key_code, 61); // TAB +} + +#[test] +fn test_parse_all_key_entries() { + let map = load_key_layout_map("Generic.kl"); + + // Test various key types + let test_cases = vec![ + (1, 111), // ESCAPE + (15, 61), // TAB + (28, 66), // ENTER + (57, 62), // SPACE + (59, 131), // F1 + (70, 116), // SCROLL_LOCK + ]; + + for (scan_code, expected_key_code) in test_cases { + let key_code = map + .map_key(scan_code) + .unwrap_or_else(|| panic!("Scan code {} should be mapped", scan_code)); + assert_eq!( + key_code, expected_key_code, + "Scan code {} should map to key code {}", + scan_code, expected_key_code + ); + } +} + +#[test] +fn test_parse_axis_entries() { + let map = load_key_layout_map("Generic.kl"); + + // Test axis mappings + let axis_info = map.map_axis(0x00).expect("Axis 0x00 should exist"); + assert_eq!(axis_info.axis, 0); // X axis + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + + let axis_info = map.map_axis(0x01).expect("Axis 0x01 should exist"); + assert_eq!(axis_info.axis, 1); // Y axis + + let axis_info = map.map_axis(0x03).expect("Axis 0x03 should exist"); + assert_eq!(axis_info.axis, 12); // RX axis +} + +#[test] +fn test_error_handling_malformed_file() { + // Test with invalid key entry + let contents = "key invalid ESCAPE\n"; + let result = KeyLayoutMap::load_from_contents(contents); + assert!(result.is_err(), "Should fail to parse invalid scan code"); + + // Test with missing key code + let contents = "key 1\n"; + let result = KeyLayoutMap::load_from_contents(contents); + // This should succeed but the key won't be inserted (unknown key code) + assert!(result.is_ok()); + + // Test with duplicate scan code + let contents = "key 1 ESCAPE\nkey 1 TAB\n"; + let result = KeyLayoutMap::load_from_contents(contents); + assert!(result.is_err(), "Should fail on duplicate scan code"); +} + +#[test] +fn test_map_key_with_scan_codes() { + let map = load_key_layout_map("Generic.kl"); + + // Test mapping various scan codes + let key_code = map.map_key(1).unwrap(); + assert_eq!(key_code, 111); // ESCAPE + + // Test a key with FUNCTION flag (flags are now ignored) + let key_code = map.map_key(465).unwrap(); + assert_eq!(key_code, 111); // ESCAPE + + // Test that non-existent scan code returns None + assert!(map.map_key(9999).is_none()); +} + +#[test] +fn test_map_axis_functionality() { + let map = load_key_layout_map("Generic.kl"); + + // Test normal axis + let axis_info = map.map_axis(0x00).unwrap(); + assert_eq!(axis_info.mode, KeyLayoutAxisMode::Normal); + assert_eq!(axis_info.axis, 0); + assert_eq!(axis_info.high_axis, None); + assert_eq!(axis_info.split_value, None); + assert_eq!(axis_info.flat_override, None); + + // Test that non-existent axis returns None + assert!(map.map_axis(9999).is_none()); +} + +#[test] +fn test_find_scan_code_for_key() { + let map = load_key_layout_map("Generic.kl"); + + // Find scan code for ESCAPE - returns first scan code (1, not 465 with FUNCTION flag) + let scan_code = map.find_scan_code_for_key(111); // ESCAPE + assert_eq!(scan_code, Some(1), "Should find scan code 1 for ESCAPE"); + + // Find scan code for a common key + let scan_code = map.find_scan_code_for_key(8); // KEYCODE_1 + assert_eq!(scan_code, Some(2), "Should find scan code 2 for KEYCODE_1"); + + // Non-existent key code should return None + assert!(map.find_scan_code_for_key(9999).is_none()); +} + +#[test] +fn test_reverse_mapping_basic() { + let contents = "key 1 ESCAPE\nkey 2 1\nkey 100 ESCAPE\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + // ESCAPE (111) returns the first scan code (1) + let scan_code = map.find_scan_code_for_key(111); + assert_eq!( + scan_code, + Some(1), + "Should return first scan code for ESCAPE" + ); + + // KEYCODE_1 (8) should have scan code 2 + let scan_code = map.find_scan_code_for_key(8); + assert_eq!( + scan_code, + Some(2), + "Should return scan code 2 for KEYCODE_1" + ); + + // Non-existent key code should return None + assert!( + map.find_scan_code_for_key(9999).is_none(), + "Non-existent key code should return None" + ); +} + +#[test] +fn test_reverse_mapping_keeps_first_scan_code() { + // When multiple scan codes map to the same key code, we keep the first one + let contents = "key 1 ESCAPE\nkey 465 ESCAPE FUNCTION\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + // Should return the first scan code (1), not 465 + let scan_code = map.find_scan_code_for_key(111); + assert_eq!( + scan_code, + Some(1), + "Should return first scan code for ESCAPE" + ); +} + +#[test] +fn test_bidirectional_mapping() { + let contents = "key 1 ESCAPE\nkey 2 1\nkey 28 ENTER\n"; + let map = KeyLayoutMap::load_from_contents(contents).unwrap(); + + // Forward mapping + assert_eq!(map.map_key(1), Some(111)); // ESCAPE + assert_eq!(map.map_key(2), Some(8)); // KEYCODE_1 + assert_eq!(map.map_key(28), Some(66)); // ENTER + + // Reverse mapping + assert_eq!(map.find_scan_code_for_key(111), Some(1)); // ESCAPE -> 1 + assert_eq!(map.find_scan_code_for_key(8), Some(2)); // KEYCODE_1 -> 2 + assert_eq!(map.find_scan_code_for_key(66), Some(28)); // ENTER -> 28 +} + +#[test] +fn test_load_all_files_in_test_data() { + let test_data_path = get_test_data_path(); + let test_data_dir = Path::new(&test_data_path); + if !test_data_dir.exists() { + panic!("Test data directory does not exist: {}", test_data_path); + } + + // Use glob pattern to recursively find all .kl files + let pattern = format!("{}/**/*.kl", test_data_path); + let entries = glob(&pattern).expect("Failed to read glob pattern"); + + for entry in entries { + let path = entry.expect("Failed to read glob entry"); + println!("Testing file: {:?}", path.clone()); + + let result = KeyLayoutMap::load_from_file(path.clone()); + assert!( + result.is_ok(), + "Failed to load key layout file: {:?} - {:?}", + path.clone(), + result.err() + ); + + // At least verify it's not empty + result.unwrap(); + // We can't check exact counts as files may vary + println!(" Loaded successfully"); + } +} diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/6t/gpio-keys.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/6t/gpio-keys.kl new file mode 100644 index 0000000000..f8dbf8577f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/6t/gpio-keys.kl @@ -0,0 +1,40 @@ +# Copyright (c) 2013, The Linux Foundation. All rights reserved. +# +# Redistribution and use in source and binary forms, with or without +# modification, are permitted provided that the following conditions are +# met: +# * Redistributions of source code must retain the above copyright +# notice, this list of conditions and the following disclaimer. +# * Redistributions in binary form must reproduce the above +# copyright notice, this list of conditions and the following +# disclaimer in the documentation and/or other materials provided +# with the distribution. +# * Neither the name of The Linux Foundation nor the names of its +# contributors may be used to endorse or promote products derived +# from this software without specific prior written permission. +# +# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED +# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT +# ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS +# BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +# CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +# SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR +# BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, +# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE +# OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN +# IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +key 115 VOLUME_UP +key 114 VOLUME_DOWN +key 102 HOME +key 528 FOCUS +key 766 CAMERA + +# ifdef ODM_HQ_EDIT +# Add for N1 by yutieshuan at 2020/06/13 start +key 752 RF_CABLE_MID_OUT WAKE_DROPPED +key 753 RF_CABLE_MID_IN WAKE_DROPPED +key 754 RF_CABLE_HD_OUT WAKE_DROPPED +key 755 RF_CABLE_HD_IN WAKE_DROPPED +# endif diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl new file mode 100644 index 0000000000..31092536ba --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/Generic.kl @@ -0,0 +1,482 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Generic key layout file for full alphabetic US English PC style external keyboards. +# +# This file is intentionally very generic and is intended to support a broad range of keyboards. +# Do not edit the generic key layout to support a specific keyboard; instead, create +# a new key layout file with the required keyboard configuration. +# + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 55 NUMPAD_MULTIPLY +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 69 NUM_LOCK +key 70 SCROLL_LOCK +key 71 NUMPAD_7 +key 72 NUMPAD_8 +key 73 NUMPAD_9 +key 74 NUMPAD_SUBTRACT +key 75 NUMPAD_4 +key 76 NUMPAD_5 +key 77 NUMPAD_6 +key 78 NUMPAD_ADD +key 79 NUMPAD_1 +key 80 NUMPAD_2 +key 81 NUMPAD_3 +key 82 NUMPAD_0 +key 83 NUMPAD_DOT +# key 84 (undefined) +key 85 ZENKAKU_HANKAKU +key 86 BACKSLASH +key 87 F11 +key 88 F12 +key 89 RO +# key 90 "KEY_KATAKANA" +# key 91 "KEY_HIRAGANA" +key 92 HENKAN +key 93 KATAKANA_HIRAGANA +key 94 MUHENKAN +key 95 NUMPAD_COMMA +key 96 NUMPAD_ENTER +key 97 CTRL_RIGHT +key 98 NUMPAD_DIVIDE +key 99 SYSRQ +key 100 ALT_RIGHT +# key 101 "KEY_LINEFEED" +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 INSERT +key 111 FORWARD_DEL +# key 112 "KEY_MACRO" +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 116 POWER +key 117 NUMPAD_EQUALS +# key 118 "KEY_KPPLUSMINUS" +key 119 BREAK +key 120 RECENT_APPS +key 121 NUMPAD_COMMA +key 122 KANA +key 123 EISU +key 124 YEN +key 125 META_LEFT +key 126 META_RIGHT +key 127 MENU +key 128 MEDIA_STOP +# key 129 "KEY_AGAIN" +# key 130 "KEY_PROPS" +# key 131 "KEY_UNDO" +# key 132 "KEY_FRONT" +key 133 COPY +# key 134 "KEY_OPEN" +key 135 PASTE +# key 136 "KEY_FIND" +key 137 CUT +# key 138 "KEY_HELP" +key 139 MENU +key 140 CALCULATOR +# key 141 "KEY_SETUP" +key 142 SLEEP +key 143 WAKEUP +# key 144 "KEY_FILE" +# key 145 "KEY_SENDFILE" +# key 146 "KEY_DELETEFILE" +# key 147 "KEY_XFER" +# key 148 "KEY_PROG1" +# key 149 "KEY_PROG2" +key 150 EXPLORER +# key 151 "KEY_MSDOS" +key 152 POWER +# key 153 "KEY_DIRECTION" +# key 154 "KEY_CYCLEWINDOWS" +key 155 ENVELOPE +key 156 BOOKMARK +# key 157 "KEY_COMPUTER" +key 158 BACK +key 159 FORWARD +key 160 MEDIA_CLOSE +key 161 MEDIA_EJECT +key 162 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 169 CALL +# key 170 "KEY_ISO" +key 171 MUSIC +key 172 HOME +key 173 REFRESH +# key 174 "KEY_EXIT" +# key 175 "KEY_MOVE" +# key 176 "KEY_EDIT" +key 177 PAGE_UP +key 178 PAGE_DOWN +key 179 NUMPAD_LEFT_PAREN +key 180 NUMPAD_RIGHT_PAREN +# key 181 "KEY_NEW" +# key 182 "KEY_REDO" +# key 183 F13 +# key 184 F14 +# key 185 F15 +# key 186 F16 +# key 187 F17 +# key 188 F18 +# key 189 F19 +# key 190 F20 +# key 191 F21 +# key 192 F22 +# key 193 F23 +# key 194 F24 +# key 195 (undefined) +# key 196 (undefined) +# key 197 (undefined) +# key 198 (undefined) +# key 199 (undefined) +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +# key 202 "KEY_PROG3" +# key 203 "KEY_PROG4" +key 204 NOTIFICATION +# key 205 "KEY_SUSPEND" +# key 206 "KEY_CLOSE" +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 209 "KEY_BASSBOOST" +# key 210 "KEY_PRINT" +# key 211 "KEY_HP" +key 212 CAMERA +key 213 MUSIC +# key 214 "KEY_QUESTION" +key 215 ENVELOPE +# key 216 "KEY_CHAT" +key 217 SEARCH +# key 218 "KEY_CONNECT" +# key 219 "KEY_FINANCE" +# key 220 "KEY_SPORT" +# key 221 "KEY_SHOP" +# key 222 "KEY_ALTERASE" +# key 223 "KEY_CANCEL" +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +key 226 HEADSETHOOK +key 228 KEYBOARD_BACKLIGHT_TOGGLE +key 229 KEYBOARD_BACKLIGHT_DOWN +key 230 KEYBOARD_BACKLIGHT_UP +key 248 MUTE + +key 256 BUTTON_1 +key 257 BUTTON_2 +key 258 BUTTON_3 +key 259 BUTTON_4 +key 260 BUTTON_5 +key 261 BUTTON_6 +key 262 BUTTON_7 +key 263 BUTTON_8 +key 264 BUTTON_9 +key 265 BUTTON_10 +key 266 BUTTON_11 +key 267 BUTTON_12 +key 268 BUTTON_13 +key 269 BUTTON_14 +key 270 BUTTON_15 +key 271 BUTTON_16 + +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_C +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_Z +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + + +key 329 STYLUS_BUTTON_TERTIARY +key 331 STYLUS_BUTTON_PRIMARY +key 332 STYLUS_BUTTON_SECONDARY + + +# key 352 "KEY_OK" +key 353 DPAD_CENTER +# key 354 "KEY_GOTO" +# key 355 "KEY_CLEAR" +# key 356 "KEY_POWER2" +# key 357 "KEY_OPTION" +# key 358 "KEY_INFO" +# key 359 "KEY_TIME" +# key 360 "KEY_VENDOR" +# key 361 "KEY_ARCHIVE" +key 362 GUIDE +# key 363 "KEY_CHANNEL" +# key 364 "KEY_FAVORITES" +# key 365 "KEY_EPG" +key 366 DVR +# key 367 "KEY_MHP" +# key 368 "KEY_LANGUAGE" +# key 369 "KEY_TITLE" +key 370 CAPTIONS +# key 371 "KEY_ANGLE" +# key 372 "KEY_ZOOM" +# key 373 "KEY_MODE" +# key 374 "KEY_KEYBOARD" +# key 375 "KEY_SCREEN" +# key 376 "KEY_PC" +key 377 TV +# key 378 "KEY_TV2" +# key 379 "KEY_VCR" +# key 380 "KEY_VCR2" +# key 381 "KEY_SAT" +# key 382 "KEY_SAT2" +# key 383 "KEY_CD" +# key 384 "KEY_TAPE" +# key 385 "KEY_RADIO" +# key 386 "KEY_TUNER" +# key 387 "KEY_PLAYER" +# key 388 "KEY_TEXT" +# key 389 "KEY_DVD" +# key 390 "KEY_AUX" +# key 391 "KEY_MP3" +# key 392 "KEY_AUDIO" +# key 393 "KEY_VIDEO" +# key 394 "KEY_DIRECTORY" +# key 395 "KEY_LIST" +# key 396 "KEY_MEMO" +key 397 CALENDAR +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +# key 404 "KEY_FIRST" +key 405 LAST_CHANNEL +# key 406 "KEY_AB" +# key 407 "KEY_NEXT" +# key 408 "KEY_RESTART" +# key 409 "KEY_SLOW" +# key 410 "KEY_SHUFFLE" +# key 411 "KEY_BREAK" +# key 412 "KEY_PREVIOUS" +# key 413 "KEY_DIGITS" +# key 414 "KEY_TEEN" +# key 415 "KEY_TWEN" +# key 418 "KEY_ZOOM_IN" +key 418 ZOOM_IN +# key 419 "KEY_ZOOM_OUT" +key 419 ZOOM_OUT +key 528 FOCUS + +key 429 CONTACTS + +# key 448 "KEY_DEL_EOL" +# key 449 "KEY_DEL_EOS" +# key 450 "KEY_INS_LINE" +# key 451 "KEY_DEL_LINE" + + +key 464 FUNCTION +key 465 ESCAPE FUNCTION +key 466 F1 FUNCTION +key 467 F2 FUNCTION +key 468 F3 FUNCTION +key 469 F4 FUNCTION +key 470 F5 FUNCTION +key 471 F6 FUNCTION +key 472 F7 FUNCTION +key 473 F8 FUNCTION +key 474 F9 FUNCTION +key 475 F10 FUNCTION +key 476 F11 FUNCTION +key 477 F12 FUNCTION +key 478 1 FUNCTION +key 479 2 FUNCTION +key 480 D FUNCTION +key 481 E FUNCTION +key 482 F FUNCTION +key 483 S FUNCTION +key 484 B FUNCTION + + +# key 497 KEY_BRL_DOT1 +# key 498 KEY_BRL_DOT2 +# key 499 KEY_BRL_DOT3 +# key 500 KEY_BRL_DOT4 +# key 501 KEY_BRL_DOT5 +# key 502 KEY_BRL_DOT6 +# key 503 KEY_BRL_DOT7 +# key 504 KEY_BRL_DOT8 + +key 522 STAR +key 523 POUND +key 580 APP_SWITCH +key 582 VOICE_ASSIST +# Linux KEY_ASSISTANT +key 583 ASSIST +key 656 MACRO_1 +key 657 MACRO_2 +key 658 MACRO_3 +key 659 MACRO_4 + +# Keys defined by HID usages +key usage 0x0c0067 WINDOW +key usage 0x0c006F BRIGHTNESS_UP +key usage 0x0c0070 BRIGHTNESS_DOWN +key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP +key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN +key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE +key usage 0x0c0173 MEDIA_AUDIO_TRACK +key usage 0x0c019C PROFILE_SWITCH +key usage 0x0c01A2 ALL_APPS +# TODO(b/297094448): Add stylus button mappings as a fallback when we have a way to determine +# if a device can actually report it. +# key usage 0x0d0044 STYLUS_BUTTON_PRIMARY +# key usage 0x0d005a STYLUS_BUTTON_SECONDARY + +# Joystick and game controller axes. +# Axes that are not mapped will be assigned generic axis numbers by the input subsystem. +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x06 THROTTLE +axis 0x07 RUDDER +axis 0x08 WHEEL +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# LEDs +led 0x00 NUM_LOCK +led 0x01 CAPS_LOCK +led 0x02 SCROLL_LOCK +led 0x03 COMPOSE +led 0x04 KANA +led 0x05 SLEEP +led 0x06 SUSPEND +led 0x07 MUTE +led 0x08 MISC +led 0x09 MAIL +led 0x0a CHARGING + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/AVRCP.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/AVRCP.kl new file mode 100644 index 0000000000..ccd0209a73 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/AVRCP.kl @@ -0,0 +1,23 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Key layout used for Bluetooth AVRCP support. + +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +key 166 MEDIA_STOP +key 163 MEDIA_NEXT +key 165 MEDIA_PREVIOUS +key 168 MEDIA_REWIND +key 208 MEDIA_FAST_FORWARD diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Generic.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Generic.kl new file mode 100644 index 0000000000..2b0802b54c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Generic.kl @@ -0,0 +1,496 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Generic key layout file for full alphabetic US English PC style external keyboards. +# +# This file is intentionally very generic and is intended to support a broad range of keyboards. +# Do not edit the generic key layout to support a specific keyboard; instead, create +# a new key layout file with the required keyboard configuration. +# + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 55 NUMPAD_MULTIPLY +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 69 NUM_LOCK +key 70 SCROLL_LOCK +key 71 NUMPAD_7 +key 72 NUMPAD_8 +key 73 NUMPAD_9 +key 74 NUMPAD_SUBTRACT +key 75 NUMPAD_4 +key 76 NUMPAD_5 +key 77 NUMPAD_6 +key 78 NUMPAD_ADD +key 79 NUMPAD_1 +key 80 NUMPAD_2 +key 81 NUMPAD_3 +key 82 NUMPAD_0 +key 83 NUMPAD_DOT +# key 84 (undefined) +key 85 ZENKAKU_HANKAKU +key 86 BACKSLASH +key 87 F11 +key 88 F12 +key 89 RO +# key 90 "KEY_KATAKANA" +# key 91 "KEY_HIRAGANA" +key 92 HENKAN +key 93 KATAKANA_HIRAGANA +key 94 MUHENKAN +key 95 NUMPAD_COMMA +key 96 NUMPAD_ENTER +key 97 CTRL_RIGHT +key 98 NUMPAD_DIVIDE +key 99 SYSRQ +key 100 ALT_RIGHT +# key 101 "KEY_LINEFEED" +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 INSERT +key 111 FORWARD_DEL +# key 112 "KEY_MACRO" +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 116 POWER +key 117 NUMPAD_EQUALS +# key 118 "KEY_KPPLUSMINUS" +key 119 BREAK +key 120 RECENT_APPS +key 121 NUMPAD_COMMA +key 122 KANA +key 123 EISU +key 124 YEN +key 125 META_LEFT +key 126 META_RIGHT +key 127 MENU +key 128 MEDIA_STOP +# key 129 "KEY_AGAIN" +# key 130 "KEY_PROPS" +# key 131 "KEY_UNDO" +# key 132 "KEY_FRONT" +key 133 COPY +# key 134 "KEY_OPEN" +key 135 PASTE +# key 136 "KEY_FIND" +key 137 CUT +# key 138 "KEY_HELP" +key 139 MENU +key 140 CALCULATOR +# key 141 "KEY_SETUP" +key 142 SLEEP +key 143 WAKEUP +# key 144 "KEY_FILE" +# key 145 "KEY_SENDFILE" +# key 146 "KEY_DELETEFILE" +# key 147 "KEY_XFER" +# key 148 "KEY_PROG1" +# key 149 "KEY_PROG2" +key 150 EXPLORER +# key 151 "KEY_MSDOS" +key 152 LOCK +# key 153 "KEY_DIRECTION" +# key 154 "KEY_CYCLEWINDOWS" +key 155 ENVELOPE +key 156 BOOKMARK +# key 157 "KEY_COMPUTER" +key 158 BACK +key 159 FORWARD +key 160 MEDIA_CLOSE +key 161 MEDIA_EJECT +key 162 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 169 CALL +# key 170 "KEY_ISO" +key 171 MUSIC +key 172 HOME +key 173 REFRESH +# key 174 "KEY_EXIT" +# key 175 "KEY_MOVE" +# key 176 "KEY_EDIT" +key 177 PAGE_UP +key 178 PAGE_DOWN +key 179 NUMPAD_LEFT_PAREN +key 180 NUMPAD_RIGHT_PAREN +key 181 NEW +# key 182 "KEY_REDO" +key 183 F13 +key 184 F14 +key 185 F15 +key 186 F16 +key 187 F17 +key 188 F18 +key 189 F19 +key 190 F20 +key 191 F21 +key 192 F22 +key 193 F23 +key 194 F24 +# key 195 (undefined) +# key 196 (undefined) +# key 197 (undefined) +# key 198 (undefined) +# key 199 (undefined) +key 200 MEDIA_PLAY +key 201 MEDIA_PAUSE +# key 202 "KEY_PROG3" +# key 203 "KEY_PROG4" +key 204 NOTIFICATION +# key 205 "KEY_SUSPEND" +key 206 CLOSE +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 209 "KEY_BASSBOOST" +key 210 PRINT +# key 211 "KEY_HP" +key 212 CAMERA +key 213 MUSIC +# key 214 "KEY_QUESTION" +key 215 ENVELOPE +# key 216 "KEY_CHAT" +key 217 SEARCH +# key 218 "KEY_CONNECT" +# key 219 "KEY_FINANCE" +# key 220 "KEY_SPORT" +# key 221 "KEY_SHOP" +# key 222 "KEY_ALTERASE" +# key 223 "KEY_CANCEL" +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +key 226 HEADSETHOOK +key 228 KEYBOARD_BACKLIGHT_TOGGLE +key 229 KEYBOARD_BACKLIGHT_DOWN +key 230 KEYBOARD_BACKLIGHT_UP +key 248 MUTE + +key 256 BUTTON_1 +key 257 BUTTON_2 +key 258 BUTTON_3 +key 259 BUTTON_4 +key 260 BUTTON_5 +key 261 BUTTON_6 +key 262 BUTTON_7 +key 263 BUTTON_8 +key 264 BUTTON_9 +key 265 BUTTON_10 +key 266 BUTTON_11 +key 267 BUTTON_12 +key 268 BUTTON_13 +key 269 BUTTON_14 +key 270 BUTTON_15 +key 271 BUTTON_16 + +key 288 BUTTON_1 +key 289 BUTTON_2 +key 290 BUTTON_3 +key 291 BUTTON_4 +key 292 BUTTON_5 +key 293 BUTTON_6 +key 294 BUTTON_7 +key 295 BUTTON_8 +key 296 BUTTON_9 +key 297 BUTTON_10 +key 298 BUTTON_11 +key 299 BUTTON_12 +key 300 BUTTON_13 +key 301 BUTTON_14 +key 302 BUTTON_15 +key 303 BUTTON_16 + + +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_C +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_Z +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + + +key 329 STYLUS_BUTTON_TERTIARY +key 331 STYLUS_BUTTON_PRIMARY +key 332 STYLUS_BUTTON_SECONDARY + + +# key 352 "KEY_OK" +key 353 DPAD_CENTER +# key 354 "KEY_GOTO" +# key 355 "KEY_CLEAR" +# key 356 "KEY_POWER2" +# key 357 "KEY_OPTION" +# key 358 "KEY_INFO" +# key 359 "KEY_TIME" +# key 360 "KEY_VENDOR" +# key 361 "KEY_ARCHIVE" +key 362 GUIDE +# key 363 "KEY_CHANNEL" +# key 364 "KEY_FAVORITES" +# key 365 "KEY_EPG" +key 366 DVR +# key 367 "KEY_MHP" +key 368 LANGUAGE_SWITCH +# key 369 "KEY_TITLE" +key 370 CAPTIONS +# key 371 "KEY_ANGLE" +key 372 FULLSCREEN +# key 373 "KEY_MODE" +# key 374 "KEY_KEYBOARD" +# key 375 "KEY_SCREEN" +# key 376 "KEY_PC" +key 377 TV +# key 378 "KEY_TV2" +# key 379 "KEY_VCR" +# key 380 "KEY_VCR2" +# key 381 "KEY_SAT" +# key 382 "KEY_SAT2" +# key 383 "KEY_CD" +# key 384 "KEY_TAPE" +# key 385 "KEY_RADIO" +# key 386 "KEY_TUNER" +# key 387 "KEY_PLAYER" +# key 388 "KEY_TEXT" +# key 389 "KEY_DVD" +# key 390 "KEY_AUX" +# key 391 "KEY_MP3" +# key 392 "KEY_AUDIO" +# key 393 "KEY_VIDEO" +# key 394 "KEY_DIRECTORY" +# key 395 "KEY_LIST" +# key 396 "KEY_MEMO" +key 397 CALENDAR +key 398 PROG_RED +key 399 PROG_GREEN +key 400 PROG_YELLOW +key 401 PROG_BLUE +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +# key 404 "KEY_FIRST" +key 405 LAST_CHANNEL +# key 406 "KEY_AB" +# key 407 "KEY_NEXT" +# key 408 "KEY_RESTART" +# key 409 "KEY_SLOW" +# key 410 "KEY_SHUFFLE" +# key 411 "KEY_BREAK" +# key 412 "KEY_PREVIOUS" +# key 413 "KEY_DIGITS" +# key 414 "KEY_TEEN" +# key 415 "KEY_TWEN" +# key 418 "KEY_ZOOM_IN" +key 418 ZOOM_IN +# key 419 "KEY_ZOOM_OUT" +key 419 ZOOM_OUT +key 528 FOCUS + +key 429 CONTACTS + +# key 448 "KEY_DEL_EOL" +# key 449 "KEY_DEL_EOS" +# key 450 "KEY_INS_LINE" +# key 451 "KEY_DEL_LINE" + + +key 464 FUNCTION +key 465 ESCAPE FUNCTION +key 466 F1 FUNCTION +key 467 F2 FUNCTION +key 468 F3 FUNCTION +key 469 F4 FUNCTION +key 470 F5 FUNCTION +key 471 F6 FUNCTION +key 472 F7 FUNCTION +key 473 F8 FUNCTION +key 474 F9 FUNCTION +key 475 F10 FUNCTION +key 476 F11 FUNCTION +key 477 F12 FUNCTION +key 478 1 FUNCTION +key 479 2 FUNCTION +key 480 D FUNCTION +key 481 E FUNCTION +key 482 F FUNCTION +key 483 S FUNCTION +key 484 B FUNCTION + + +# key 497 KEY_BRL_DOT1 +# key 498 KEY_BRL_DOT2 +# key 499 KEY_BRL_DOT3 +# key 500 KEY_BRL_DOT4 +# key 501 KEY_BRL_DOT5 +# key 502 KEY_BRL_DOT6 +# key 503 KEY_BRL_DOT7 +# key 504 KEY_BRL_DOT8 + +key 522 STAR +key 523 POUND +key 580 APP_SWITCH +key 582 VOICE_ASSIST +# Linux KEY_ASSISTANT +key 583 ASSIST +key 585 EMOJI_PICKER +key 586 DICTATE +key 656 MACRO_1 +key 657 MACRO_2 +key 658 MACRO_3 +key 659 MACRO_4 + +# Keys defined by HID usages +key usage 0x010082 LOCK FALLBACK_USAGE_MAPPING +key usage 0x01009B DO_NOT_DISTURB FALLBACK_USAGE_MAPPING +key usage 0x0c0065 SCREENSHOT FALLBACK_USAGE_MAPPING +key usage 0x0c0067 WINDOW FALLBACK_USAGE_MAPPING +key usage 0x0c006F BRIGHTNESS_UP FALLBACK_USAGE_MAPPING +key usage 0x0c0070 BRIGHTNESS_DOWN FALLBACK_USAGE_MAPPING +key usage 0x0c0079 KEYBOARD_BACKLIGHT_UP FALLBACK_USAGE_MAPPING +key usage 0x0c007A KEYBOARD_BACKLIGHT_DOWN FALLBACK_USAGE_MAPPING +key usage 0x0c007C KEYBOARD_BACKLIGHT_TOGGLE FALLBACK_USAGE_MAPPING +key usage 0x0c00D8 DICTATE FALLBACK_USAGE_MAPPING +key usage 0x0c00D9 EMOJI_PICKER FALLBACK_USAGE_MAPPING +key usage 0x0c0173 MEDIA_AUDIO_TRACK FALLBACK_USAGE_MAPPING +key usage 0x0c019C PROFILE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c019F SETTINGS FALLBACK_USAGE_MAPPING +key usage 0x0c01A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c0201 NEW FALLBACK_USAGE_MAPPING +key usage 0x0c0203 CLOSE FALLBACK_USAGE_MAPPING +key usage 0x0c0208 PRINT FALLBACK_USAGE_MAPPING +key usage 0x0c0227 REFRESH FALLBACK_USAGE_MAPPING +key usage 0x0c0232 FULLSCREEN FALLBACK_USAGE_MAPPING +key usage 0x0c029D LANGUAGE_SWITCH FALLBACK_USAGE_MAPPING +key usage 0x0c029F RECENT_APPS FALLBACK_USAGE_MAPPING +key usage 0x0c02A2 ALL_APPS FALLBACK_USAGE_MAPPING +key usage 0x0d0044 STYLUS_BUTTON_PRIMARY FALLBACK_USAGE_MAPPING +key usage 0x0d005a STYLUS_BUTTON_SECONDARY FALLBACK_USAGE_MAPPING + +# Joystick and game controller axes. +# Axes that are not mapped will be assigned generic axis numbers by the input subsystem. +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x06 THROTTLE +axis 0x07 RUDDER +axis 0x08 WHEEL +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# LEDs +led 0x00 NUM_LOCK +led 0x01 CAPS_LOCK +led 0x02 SCROLL_LOCK +led 0x03 COMPOSE +led 0x04 KANA +led 0x05 SLEEP +led 0x06 SUSPEND +led 0x07 MUTE +led 0x08 MISC +led 0x09 MAIL +led 0x0a CHARGING + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_0011.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_0011.kl new file mode 100644 index 0000000000..32f8c829cd --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_0011.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Classic [S]NES Controller + +key 288 BUTTON_X +key 289 BUTTON_A +key 290 BUTTON_B +key 291 BUTTON_Y +key 292 BUTTON_L1 +key 293 BUTTON_R1 +key 297 BUTTON_START +key 296 BUTTON_SELECT + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_18d4.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_18d4.kl new file mode 100644 index 0000000000..b9a2b67afb --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0079_Product_18d4.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# GPD Win 2 X-Box Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0171_Product_0419.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0171_Product_0419.kl new file mode 100644 index 0000000000..05a25f0210 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0171_Product_0419.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Amazon Luna Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x02 Z flat 4096 +axis 0x05 RZ flat 4096 + +# Triggers. +axis 0x0a LTRIGGER +axis 0x09 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Action button (circle icon, left of the Home button) +key 158 BUTTON_SELECT + +# Home button (branded button in the center of the controller) +key 172 BUTTON_MODE + +# Menu button (hamburger icon, right of the Home button) +key 315 BUTTON_START + +# Alexa Push-To-Talk button (microphone icon, below the Home button) +key 217 MEDIA_RECORD diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0428_Product_4001.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0428_Product_4001.kl new file mode 100644 index 0000000000..7d1dd12803 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0428_Product_4001.kl @@ -0,0 +1,27 @@ +# Gravis GamePad Pro USB + +# Yellow +key 0x131 BUTTON_A +# Green +key 0x132 BUTTON_B +# Red +key 0x130 BUTTON_X +# Blue +key 0x133 BUTTON_Y + +# Left Upper Shoulder "1" +key 0x134 BUTTON_L1 +# Right Upper Shoulder "1" +key 0x135 BUTTON_R1 +# Left Lower Shoulder "2" +key 0x136 BUTTON_L2 +# Right Lower Shoulder "2" +key 0x137 BUTTON_R2 + +# Select & Start +key 0x138 BUTTON_SELECT +key 0x139 BUTTON_START + +# D-Pad +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_044f_Product_b326.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_044f_Product_b326.kl new file mode 100644 index 0000000000..d248d71356 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_044f_Product_b326.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Thrustmaster Gamepad GP XID +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028e.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028e.kl new file mode 100644 index 0000000000..e4f48f9fb3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028e.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox 360 USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028f.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028f.kl new file mode 100644 index 0000000000..cc5b33b9a1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_028f.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Microsoft X-Box 360 pad v2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02a1.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02a1.kl new file mode 100644 index 0000000000..0214361717 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02a1.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox 360 Wireless Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02d1.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02d1.kl new file mode 100644 index 0000000000..0867670759 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02d1.kl @@ -0,0 +1,57 @@ +# Copyright (C) 2016 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox One Controller - Model 1537 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02dd.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02dd.kl new file mode 100644 index 0000000000..3975cec24f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02dd.kl @@ -0,0 +1,57 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox One Controller - Model 1697 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e0.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e0.kl new file mode 100644 index 0000000000..1dd8e157c5 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e0.kl @@ -0,0 +1,59 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox Wireless Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x132 BUTTON_X +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 + +# LT axis +axis 0x02 LTRIGGER +# RT axis +axis 0x05 RTRIGGER + + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x138 BUTTON_THUMBL +# Right stick click +key 0x139 BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 0x136 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 0x137 BUTTON_START + +# Xbox key +key 0x8b BUTTON_MODE \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e3.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e3.kl new file mode 100644 index 0000000000..0a6e7d75c2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02e3.kl @@ -0,0 +1,56 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Microsoft X-Box One Elite Pad - Model 1698 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x133 BUTTON_X +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left stick +axis 0x00 X +axis 0x01 Y +# Right stick +axis 0x03 Z +axis 0x04 RZ + +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Two overlapping rectangles +key 0x13a BUTTON_SELECT +# Hamburger - 3 parallel lines +key 0x13b BUTTON_START + +# Xbox key +key 0x13c BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02ea.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02ea.kl new file mode 100644 index 0000000000..3b46db2ce9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02ea.kl @@ -0,0 +1,57 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox One Controller - Model 1708 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02fd.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02fd.kl new file mode 100644 index 0000000000..1b03497ae3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_02fd.kl @@ -0,0 +1,62 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox One Controller - Model 1708 - Bluetooth +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x0a LTRIGGER +axis 0x09 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x02 Z flat 4096 +axis 0x05 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 158 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# There are at least two versions of firmware out for this controller. +# They send different linux keys for the "Xbox" button. +# Xbox key (original firmware) +key 172 BUTTON_MODE + +# Xbox key (newer firmware) +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_0b12.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_0b12.kl new file mode 100644 index 0000000000..0b44c7434a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_045e_Product_0b12.kl @@ -0,0 +1,59 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Two overlapping rectangles +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Three parallel horizontal lines (hamburger menu) +key 315 BUTTON_START + +#Button below the "X" button +key 167 MEDIA_RECORD + diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_b501.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_b501.kl new file mode 100644 index 0000000000..496ddc3eec --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_b501.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Logitech Bluetooth wireless gamepad (RedHawk) + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 + +key 314 BUTTON_SELECT +key 315 BUTTON_START +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 108 DPAD_DOWN +key 353 DPAD_CENTER + +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 152 POWER +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS + +key 158 BACK +key 172 HOME + +key 217 SEARCH +key 580 APP_SWITCH +key 582 ASSIST + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +led 0x00 CONTROLLER_1 +led 0x01 CONTROLLER_2 +led 0x02 CONTROLLER_3 +led 0x03 CONTROLLER_4 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c216.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c216.kl new file mode 100644 index 0000000000..8bc142f0ca --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c216.kl @@ -0,0 +1,37 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech Dual Action Controller +# + +key 0x121 BUTTON_A +key 0x122 BUTTON_B +key 0x120 BUTTON_X +key 0x123 BUTTON_Y +key 0x124 BUTTON_L1 +key 0x125 BUTTON_R1 +key 0x126 BUTTON_L2 +key 0x127 BUTTON_R2 +key 0x128 BACK +key 0x129 BUTTON_START +key 0x12a BUTTON_THUMBL +key 0x12b BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c219.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c219.kl new file mode 100644 index 0000000000..2fa964ce53 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c219.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Logitech Logitech Cordless RumblePad 2 + +key 305 BUTTON_A +key 306 BUTTON_B +key 304 BUTTON_X +key 307 BUTTON_Y +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 310 BUTTON_L2 +key 311 BUTTON_R2 +key 313 BUTTON_START +key 312 BACK +key 314 BUTTON_THUMBL +key 315 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21d.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21d.kl new file mode 100644 index 0000000000..3fbdecc174 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21d.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Logitech F310 + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 GAS +axis 0x02 BRAKE +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21e.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21e.kl new file mode 100644 index 0000000000..998074331d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21e.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech Gamepad F510 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21f.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21f.kl new file mode 100644 index 0000000000..a9ba3781ec --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c21f.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Logitech Wireless Gamepad F710 + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c242.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c242.kl new file mode 100644 index 0000000000..51eb44a621 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c242.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech Chillstream Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c294.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c294.kl new file mode 100644 index 0000000000..5492f49758 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c294.kl @@ -0,0 +1,53 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech G25 Racing Wheel (in Compatibility Mode) +# + +# 4 way buttons above hat +key 0x121 BUTTON_A +key 0x123 BUTTON_B +key 0x120 BUTTON_X +key 0x122 BUTTON_Y + +# Row of buttons under hat +key 0x12b BUTTON_1 +key 0x128 BUTTON_2 +key 0x129 BUTTON_3 +key 0x12a BUTTON_4 + +# Gear shift positions +# 0x12a top-left gear (aliased as BUTTON_4) +# 0x12b bottom-left gear (aliased as BUTTON_1) + +# Buttons on wheel +key 0x127 BUTTON_L1 +key 0x126 BUTTON_R1 + +# Toggles under wheel +key 0x125 BUTTON_L2 +key 0x124 BUTTON_R2 + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Steering Wheel +axis 0x00 WHEEL + +# Accelerator / Brake +# 00..7e : accelerator +# 80..ff : brake +axis 0x01 split 0x7f GAS BRAKE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c299.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c299.kl new file mode 100644 index 0000000000..d42963dbc9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c299.kl @@ -0,0 +1,62 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech G25 Racing Wheel (in Native Mode) +# + +# 4 way buttons above hat +key 0x121 BUTTON_A +key 0x123 BUTTON_B +key 0x120 BUTTON_X +key 0x122 BUTTON_Y + +# Row of buttons under hat +key 0x12b BUTTON_1 +key 0x128 BUTTON_2 +key 0x129 BUTTON_3 +key 0x12a BUTTON_4 + +# Gear shift positions +key 0x12c BUTTON_5 +key 0x12d BUTTON_6 +key 0x12e BUTTON_7 +key 0x12f BUTTON_8 +key 0x2d0 BUTTON_9 +key 0x2d1 BUTTON_10 +key 0x2d2 BUTTON_11 + +# Buttons on wheel +key 0x127 BUTTON_L1 +key 0x126 BUTTON_R1 + +# Toggles under wheel +key 0x125 BUTTON_L2 +key 0x124 BUTTON_R2 + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Steering Wheel +axis 0x00 WHEEL + +# Clutch +axis 0x01 invert GENERIC_1 + +# Accelerator +axis 0x02 invert GAS + +# Brake +axis 0x05 invert BRAKE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c532.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c532.kl new file mode 100644 index 0000000000..741c2e18b2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_046d_Product_c532.kl @@ -0,0 +1,133 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logitech Revue Wireless keyboard +# +# Notes: +# - The GRAVE key is emulated by the keyboard. +# ALT + LEFT_BRACKET produces GRAVE. +# ALT + RIGHT_BRACKET produces SHIFT + GRAVE. +# - FORWARD_DEL is produced by fn + BACKSPACE +# - PAGE_UP / PAGE_DOWN is produced by fn + CHANNEL_UP / CHANNEL_DOWN +# - The AVR / STB / TV power and input buttons seem to be non-functional +# as well as several of the other fn buttons and the PIP button? + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 56 ALT_RIGHT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 +key 96 DPAD_CENTER +key 97 CTRL_RIGHT +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 NUMPAD_ENTER +key 111 FORWARD_DEL +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 119 MEDIA_PAUSE +key 125 SEARCH +key 127 MENU +key 156 BOOKMARK +key 158 BACK +key 163 MEDIA_NEXT +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 172 HOME +key 207 MEDIA_PLAY +key 208 MEDIA_FAST_FORWARD +# key 288 left mouse button +# key 289 right mouse button (fn + button) +key 362 GUIDE +key 366 DVR +key 377 TV +key 402 CHANNEL_UP +key 403 CHANNEL_DOWN +key 418 ZOOM_IN +key 419 ZOOM_OUT + diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268.kl new file mode 100644 index 0000000000..08d1c34f9e --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268.kl @@ -0,0 +1,87 @@ +# Copyright (C) 2011 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R)3 Controller +# + +key 0x124 DPAD_UP +key 0x125 DPAD_RIGHT +key 0x126 DPAD_DOWN +key 0x127 DPAD_LEFT + +key 0x12e BUTTON_A +key 0x12d BUTTON_B +key 0x12f BUTTON_X +key 0x12c BUTTON_Y +key 0x12a BUTTON_L1 +key 0x12b BUTTON_R1 +key 0x128 BUTTON_L2 +key 0x129 BUTTON_R2 +key 0x121 BUTTON_THUMBL +key 0x122 BUTTON_THUMBR + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y + +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# DPAD +# axis 0x2c -HAT_Y +# axis 0x2d +HAT_X +# axis 0x2e +HAT_Y +# axis 0x2f -HAT_X + +# L2 trigger +axis 0x30 LTRIGGER + +# R2 trigger +axis 0x31 RTRIGGER + +# L1 trigger +# axis 0x32 + +# R1 trigger +# axis 0x33 + +# Triangle +# axis 0x34 + +# Circle +# axis 0x35 + +# Cross +# axis 0x36 + +# Square +# axis 0x37 + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select +key 0x120 BUTTON_SELECT +# Start +key 0x123 BUTTON_START +# PS key +key 0x2d0 BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8000.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8000.kl new file mode 100644 index 0000000000..d281b4bb53 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8000.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R)3 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12 +# and when connected over Bluetooth +# + +key 0x220 DPAD_UP +key 0x223 DPAD_RIGHT +key 0x221 DPAD_DOWN +key 0x222 DPAD_LEFT + +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_X +key 0x133 BUTTON_Y +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# left Analog Stick +axis 0x00 X +axis 0x01 Y + +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# L2 trigger +axis 0x02 LTRIGGER + +# R2 trigger +axis 0x05 RTRIGGER + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select +key 0x13a BUTTON_SELECT +# Start +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8100.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8100.kl new file mode 100644 index 0000000000..d281b4bb53 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8100.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R)3 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.12 +# and when connected over Bluetooth +# + +key 0x220 DPAD_UP +key 0x223 DPAD_RIGHT +key 0x221 DPAD_DOWN +key 0x222 DPAD_LEFT + +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_X +key 0x133 BUTTON_Y +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# left Analog Stick +axis 0x00 X +axis 0x01 Y + +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# L2 trigger +axis 0x02 LTRIGGER + +# R2 trigger +axis 0x05 RTRIGGER + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select +key 0x13a BUTTON_SELECT +# Start +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8111.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8111.kl new file mode 100644 index 0000000000..3eafea0f6c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0268_Version_8111.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R)3 Controller +# - Version 0x8111 is for Linux hid-sony driver >=4.12 and when +# connected over USB +# + +key 0x220 DPAD_UP +key 0x223 DPAD_RIGHT +key 0x221 DPAD_DOWN +key 0x222 DPAD_LEFT + +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_X +key 0x133 BUTTON_Y +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# left Analog Stick +axis 0x00 X +axis 0x01 Y + +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# L2 trigger +axis 0x02 LTRIGGER + +# R2 trigger +axis 0x05 RTRIGGER + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select +key 0x13a BUTTON_SELECT +# Start +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4.kl new file mode 100644 index 0000000000..c8b4fc363f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4.kl @@ -0,0 +1,78 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# L2 axis +axis 0x03 LTRIGGER +# R2 axis +axis 0x04 RTRIGGER + + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +# The touchpad for this joystick will become a separate input device in future releases +# and this button will be equivalent to left mouse button +# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions +key 0x13d BUTTON_1 + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8000.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8000.kl new file mode 100644 index 0000000000..a877c4cc75 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8000.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10 +# and when connected over Bluetooth +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8100.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8100.kl new file mode 100644 index 0000000000..a877c4cc75 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8100.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10 +# and when connected over Bluetooth +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8111.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8111.kl new file mode 100644 index 0000000000..1473c4e535 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_05c4_Version_8111.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when +# connected over USB +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc.kl new file mode 100644 index 0000000000..c8b4fc363f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc.kl @@ -0,0 +1,78 @@ +# Copyright (C) 2017 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# L2 axis +axis 0x03 LTRIGGER +# R2 axis +axis 0x04 RTRIGGER + + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +# The touchpad for this joystick will become a separate input device in future releases +# and this button will be equivalent to left mouse button +# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions +key 0x13d BUTTON_1 + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8000.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8000.kl new file mode 100644 index 0000000000..a877c4cc75 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8000.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10 +# and when connected over Bluetooth +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8100.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8100.kl new file mode 100644 index 0000000000..a877c4cc75 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8100.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8000 and 0x8100 are for Linux hid-sony driver >=4.10 +# and when connected over Bluetooth +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8111.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8111.kl new file mode 100644 index 0000000000..1473c4e535 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_09cc_Version_8111.kl @@ -0,0 +1,76 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 Controller +# - Version 0x8111 is for Linux hid-sony driver >=4.10 and when +# connected over USB +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0.kl new file mode 100644 index 0000000000..bc6fc3b5e4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0.kl @@ -0,0 +1,70 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 USB Dongle +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# L2 axis +axis 0x03 LTRIGGER +# R2 axis +axis 0x04 RTRIGGER + + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +# The touchpad for this joystick will become a separate input device in future releases +# and this button will be equivalent to left mouse button +# Therefore, map it to KEYCODE_BUTTON_1 here to allow apps to still handle this on earlier versions +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0_Version_8111.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0_Version_8111.kl new file mode 100644 index 0000000000..8b85a38b43 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ba0_Version_8111.kl @@ -0,0 +1,67 @@ +# Copyright (C) 2018 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualShock 4 USB Dongle +# - Version 0x8111 is for Linux hid-sony driver >=4.10 +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# In kernel versions >= 4.10, the touchpad is a separate input device, +# so the touchpad button click will not be covered by this layout. diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6.kl new file mode 100644 index 0000000000..411dd95219 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6.kl @@ -0,0 +1,73 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualSense Controller +# + +# Only use this key layout if we have HID_PLAYSTATION! +requires_kernel_config CONFIG_HID_PLAYSTATION + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x13a BUTTON_SELECT +# Options +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6_fallback.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6_fallback.kl new file mode 100644 index 0000000000..d1a364ce8c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0ce6_fallback.kl @@ -0,0 +1,75 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualSense Controller +# + +# Use this if HID_PLAYSTATION is not available + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 304 BUTTON_X +# Cross +key 305 BUTTON_A +# Circle +key 306 BUTTON_B +# Triangle +key 307 BUTTON_Y + +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 310 BUTTON_L2 +key 311 BUTTON_R2 + +# L2 axis +axis 0x03 LTRIGGER +# R2 axis +axis 0x04 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 314 BUTTON_THUMBL +# Right stick click +key 315 BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share / "half-sun" +key 312 BUTTON_SELECT +# Options / three horizontal lines +key 313 BUTTON_START +# PS key +key 316 BUTTON_MODE + +# Touchpad press +key 317 BUTTON_1 + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2.kl new file mode 100644 index 0000000000..a47b310328 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2.kl @@ -0,0 +1,73 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualSense Edge Controller +# + +# Only use this key layout if we have HID_PLAYSTATION! +requires_kernel_config CONFIG_HID_PLAYSTATION + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x134 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# L2 axis +axis 0x02 LTRIGGER +# R2 axis +axis 0x05 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share / "half-sun" +key 0x13a BUTTON_SELECT +# Options / three horizontal lines +key 0x13b BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2_fallback.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2_fallback.kl new file mode 100644 index 0000000000..bfebb179a3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_054c_Product_0df2_fallback.kl @@ -0,0 +1,75 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Sony Playstation(R) DualSense Edge Controller +# + +# Use this if HID_PLAYSTATION is not available + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 304 BUTTON_X +# Cross +key 305 BUTTON_A +# Circle +key 306 BUTTON_B +# Triangle +key 307 BUTTON_Y + +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 310 BUTTON_L2 +key 311 BUTTON_R2 + +# L2 axis +axis 0x03 LTRIGGER +# R2 axis +axis 0x04 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 314 BUTTON_THUMBL +# Right stick click +key 315 BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share / "half-sun" +key 312 BUTTON_SELECT +# Options / three horizontal lines +key 313 BUTTON_START +# PS key +key 316 BUTTON_MODE + +# Touchpad press +key 317 BUTTON_1 + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2004.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2004.kl new file mode 100644 index 0000000000..9eaa36d740 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2004.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Elecom JC-U3613M +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2010.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2010.kl new file mode 100644 index 0000000000..09e15eaa62 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2010.kl @@ -0,0 +1,48 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Elecom JC-U4113S in DirectInput Mode (D mode). +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 306 BUTTON_A +key 307 BUTTON_B +key 304 BUTTON_X +key 305 BUTTON_Y + +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 310 BUTTON_L2 +key 311 BUTTON_R2 + +key 312 BUTTON_THUMBL +key 313 BUTTON_THUMBR + +key 314 BACK +key 315 BUTTON_START + +# Left and right stick. +axis 0x00 X +axis 0x01 Y +axis 0x05 Z +axis 0x02 RZ + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# "Guide" button (Xbox key). +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2013.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2013.kl new file mode 100644 index 0000000000..c2a74a9fd4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_056e_Product_2013.kl @@ -0,0 +1,44 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Elecom JC-U4113S in XInput Mode (X mode). +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ + +axis 0x02 BRAKE +axis 0x05 GAS + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# "Guide" button (Xbox key). +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_057e_Product_2009.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_057e_Product_2009.kl new file mode 100644 index 0000000000..7491ee562b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_057e_Product_2009.kl @@ -0,0 +1,84 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Nintendo Switch Pro Controller - HAC-013 - Bluetooth +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Button labeled as "B" but should really produce keycode "A" +key 0x130 BUTTON_A +# Button labeled as "A" but should really produce keycode "B" +key 0x131 BUTTON_B +# Button labeled as "X" but should really product keycode "Y" +key 0x133 BUTTON_Y +# Button labeled as "Y" but should really produce keycode "X" +key 0x134 BUTTON_X + +# Button labeled as "L" +key 0x136 BUTTON_L1 +# Button labeled as "R" +key 0x137 BUTTON_R1 + +# No LT / RT axes on this controller. Instead, there are keys. +# Trigger labeled as "ZL" +key 0x138 BUTTON_L2 +# Trigger labeled as "ZR" +key 0x139 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x03 Z +axis 0x04 RZ + +# Left stick click (generates linux BTN_SELECT) +key 0x13d BUTTON_THUMBL +# Right stick click (generates linux BTN_START) +key 0x13e BUTTON_THUMBR + +# Currently, the dpad produces key events +key 0x220 DPAD_UP +key 0x221 DPAD_DOWN +key 0x222 DPAD_LEFT +key 0x223 DPAD_RIGHT + +# Hat - currently not being produced by hid-nintendo, but an upcoming patch set will change the behaviour. +# Keep these mappings in anticipation of that change +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Minus +key 0x13a BUTTON_SELECT + +# Plus +key 0x13b BUTTON_START + +# Circle +key 0x135 BUTTON_MODE + +# Home key +key 0x13c HOME + +# SENSORs +sensor 0x00 ACCELEROMETER X +sensor 0x01 ACCELEROMETER Y +sensor 0x02 ACCELEROMETER Z +sensor 0x03 GYROSCOPE X +sensor 0x04 GYROSCOPE Y +sensor 0x05 GYROSCOPE Z diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0583_Product_2060.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0583_Product_2060.kl new file mode 100644 index 0000000000..92c8a14a2f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0583_Product_2060.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# ION GO PAD + +key 288 BUTTON_A +key 289 BUTTON_B +key 290 BUTTON_X +key 291 BUTTON_Y +key 294 BUTTON_L1 +key 295 BUTTON_R1 +key 292 BUTTON_L2 +key 293 BUTTON_R2 + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_05ac_Product_0239.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_05ac_Product_0239.kl new file mode 100644 index 0000000000..b0c358e7d8 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_05ac_Product_0239.kl @@ -0,0 +1,119 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Apple Wireless Keyboard +# +# Notes: +# - Keys such as PAGE_UP and FORWARD_DEL are produced using the +# function key. +# - Special function keys for brightness control, etc. are not +# implemented here. + +key 1 ESCAPE +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 +key 100 ALT_RIGHT +key 102 MOVE_HOME +key 103 DPAD_UP +key 104 PAGE_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 109 PAGE_DOWN +key 110 NUMPAD_ENTER +key 111 FORWARD_DEL +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 120 APP_SWITCH +key 125 META_LEFT +key 126 META_RIGHT +key 161 MEDIA_EJECT +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +# key 204 show gadgets +key 224 BRIGHTNESS_DOWN +key 225 BRIGHTNESS_UP +# key 229 blank special function on F5 +# key 230 blank special function on F6 +key 464 FUNCTION diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_06a3_Product_f51a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_06a3_Product_f51a.kl new file mode 100644 index 0000000000..e52f25724f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_06a3_Product_f51a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Saitek P3600 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4716.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4716.kl new file mode 100644 index 0000000000..5f3d4aa4b2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4716.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Wired Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4718.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4718.kl new file mode 100644 index 0000000000..756e1e75fb --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4718.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Street Fighter IV FightStick SE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4726.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4726.kl new file mode 100644 index 0000000000..9d8deb36e4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4726.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4736.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4736.kl new file mode 100644 index 0000000000..c556e25fde --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4736.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz MicroCon Gamepad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4740.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4740.kl new file mode 100644 index 0000000000..cdb72683b5 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_4740.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Beat Pad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_9871.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_9871.kl new file mode 100644 index 0000000000..f404065236 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_9871.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Portable Drum +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_b726.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_b726.kl new file mode 100644 index 0000000000..05b737f8f9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_b726.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Xbox controller - MW2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_beef.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_beef.kl new file mode 100644 index 0000000000..f969e73ec5 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_beef.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz JOYTECH NEO SE Advanced GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb02.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb02.kl new file mode 100644 index 0000000000..bc2fc35ae1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb02.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Saitek Cyborg Rumble Pad - PC/Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb03.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb03.kl new file mode 100644 index 0000000000..dcbf6b7487 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb03.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Saitek P3200 Rumble Pad - PC/Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb29.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb29.kl new file mode 100644 index 0000000000..fe81d1c217 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_cb29.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Saitek Aviator Stick AV8R02 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_f738.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_f738.kl new file mode 100644 index 0000000000..2c99380756 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0738_Product_f738.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Super SFIV FightStick TE S +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_07ff_Product_ffff.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_07ff_Product_ffff.kl new file mode 100644 index 0000000000..637c01bddd --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_07ff_Product_ffff.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0001.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0001.kl new file mode 100644 index 0000000000..0241f36e9a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0001.kl @@ -0,0 +1,83 @@ +# Copyright (C) 2021 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Key Layout file for Google Reference RCU Remote. +# + +key 116 POWER WAKE +key 217 ASSIST WAKE + +key 103 DPAD_UP +key 108 DPAD_DOWN +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 353 DPAD_CENTER + +key 158 BACK +key 172 HOME WAKE + +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP + +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 + +key usage 0x00070037 PERIOD + +# custom keys +key usage 0x000c01BB TV_INPUT + +key usage 0x000c0185 TV_TELETEXT +key usage 0x000c0061 CAPTIONS + +key usage 0x000c01BD INFO + +key usage 0x000c0069 PROG_RED +key usage 0x000c006A PROG_GREEN +key usage 0x000c006C PROG_YELLOW +key usage 0x000c006B PROG_BLUE +key usage 0x000c00B4 MEDIA_SKIP_BACKWARD +key usage 0x000c00CD MEDIA_PLAY_PAUSE +key usage 0x000c00B2 MEDIA_RECORD +key usage 0x000c00B3 MEDIA_SKIP_FORWARD + +key usage 0x000c022A BOOKMARK +key usage 0x000c01A2 ALL_APPS +key usage 0x000c019C PROFILE_SWITCH + +key usage 0x000c0096 SETTINGS +key usage 0x000c009F NOTIFICATION + +key usage 0x000c008D GUIDE +key usage 0x000c0089 TV + +key usage 0x000c0187 FEATURED_APP_1 WAKE #FreeTv + +key usage 0x000c009C CHANNEL_UP +key usage 0x000c009D CHANNEL_DOWN + +key usage 0x000c0077 BUTTON_3 WAKE #YouTube +key usage 0x000c0078 BUTTON_4 WAKE #Netflix +key usage 0x000c0079 BUTTON_6 WAKE +key usage 0x000c007A BUTTON_7 WAKE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0031.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0031.kl new file mode 100644 index 0000000000..dd9fbe54c4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0957_Product_0031.kl @@ -0,0 +1,82 @@ +# Copyright 2024 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Key Layout file for Google Reference RCU Remote with customizable button. +# + +key 116 POWER WAKE +key 217 ASSIST WAKE +key 423 MACRO_1 WAKE + +key 103 DPAD_UP +key 108 DPAD_DOWN +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 353 DPAD_CENTER + +key 158 BACK +key 172 HOME WAKE + +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP + +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 + +# custom keys +key usage 0x000c01BB TV_INPUT + +key usage 0x000c0185 TV_TELETEXT +key usage 0x000c0061 CAPTIONS + +key usage 0x000c01BD INFO +key usage 0x000c0037 PERIOD + +key usage 0x000c0069 PROG_RED +key usage 0x000c006A PROG_GREEN +key usage 0x000c006C PROG_YELLOW +key usage 0x000c006B PROG_BLUE +key usage 0x000c00B4 MEDIA_SKIP_BACKWARD +key usage 0x000c00CD MEDIA_PLAY_PAUSE +key usage 0x000c00B2 MEDIA_RECORD +key usage 0x000c00B3 MEDIA_SKIP_FORWARD + +key usage 0x000c022A BOOKMARK +key usage 0x000c01A2 ALL_APPS +key usage 0x000c019C PROFILE_SWITCH + +key usage 0x000c0096 SETTINGS +key usage 0x000c009F NOTIFICATION + +key usage 0x000c008D GUIDE +key usage 0x000c0089 TV + +key usage 0x000c0187 FEATURED_APP_1 WAKE #FreeTv + +key usage 0x000c009C CHANNEL_UP +key usage 0x000c009D CHANNEL_DOWN + +key usage 0x000c0077 BUTTON_3 WAKE #YouTube +key usage 0x000c0078 BUTTON_4 WAKE #Netflix +key usage 0x000c0079 BUTTON_6 WAKE +key usage 0x000c007A BUTTON_7 WAKE \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0b05_Product_4500.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0b05_Product_4500.kl new file mode 100644 index 0000000000..a7d519e903 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0b05_Product_4500.kl @@ -0,0 +1,42 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Asus Gamepad + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +key 158 BACK +key 172 HOME + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +led 0x00 CONTROLLER_1 +led 0x01 CONTROLLER_2 +led 0x02 CONTROLLER_3 +led 0x03 CONTROLLER_4 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0113.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0113.kl new file mode 100644 index 0000000000..90e1f75d19 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0113.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Afterglow AX.1 Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_011f.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_011f.kl new file mode 100644 index 0000000000..8c63c6bdf0 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_011f.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Gamepad Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0131.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0131.kl new file mode 100644 index 0000000000..368c37606d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0131.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP EA Sports Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0133.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0133.kl new file mode 100644 index 0000000000..815902edb8 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0133.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox 360 Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0139.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0139.kl new file mode 100644 index 0000000000..8e2ae13f90 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0139.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Afterglow Prismatic Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_013a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_013a.kl new file mode 100644 index 0000000000..3f81983bdc --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_013a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0146.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0146.kl new file mode 100644 index 0000000000..6ddd056c15 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0146.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Wired Controller for Xbox One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0147.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0147.kl new file mode 100644 index 0000000000..6745b7c5c2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0147.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Marvel Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0161.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0161.kl new file mode 100644 index 0000000000..3f81983bdc --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0161.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0162.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0162.kl new file mode 100644 index 0000000000..3f81983bdc --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0162.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0163.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0163.kl new file mode 100644 index 0000000000..3f81983bdc --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0163.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Xbox One Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0164.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0164.kl new file mode 100644 index 0000000000..0fdfd32922 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0164.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Battlefield One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0165.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0165.kl new file mode 100644 index 0000000000..f9731e025f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0165.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Titanfall 2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0201.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0201.kl new file mode 100644 index 0000000000..5b4c167d99 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0201.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Pelican PL-3601 'TSZ' Wired Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0213.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0213.kl new file mode 100644 index 0000000000..9317346faa --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0213.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Afterglow Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_021f.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_021f.kl new file mode 100644 index 0000000000..f8d3f0c080 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_021f.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0246.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0246.kl new file mode 100644 index 0000000000..daf8e4525a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0246.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Gamepad for Xbox One 2015 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a4.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a4.kl new file mode 100644 index 0000000000..9ffae33bc3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a4.kl @@ -0,0 +1,54 @@ +# Copyright (C) 2019 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Wired Controller for Xbox One - Stealth Series +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a6.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a6.kl new file mode 100644 index 0000000000..99a59317e7 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02a6.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Wired Controller for Xbox One - Camo Series +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02ab.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02ab.kl new file mode 100644 index 0000000000..071a56c8c0 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_02ab.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Controller for Xbox One +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0301.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0301.kl new file mode 100644 index 0000000000..a3b982d514 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0301.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logic3 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0346.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0346.kl new file mode 100644 index 0000000000..6fefbf7ca2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0346.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Gamepad for Xbox One 2016 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0401.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0401.kl new file mode 100644 index 0000000000..a3b982d514 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0401.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Logic3 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0413.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0413.kl new file mode 100644 index 0000000000..90e1f75d19 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0413.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Afterglow AX.1 Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0501.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0501.kl new file mode 100644 index 0000000000..35831d1486 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_0501.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f501.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f501.kl new file mode 100644 index 0000000000..b46c005353 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f501.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# XBox-compatible USB Controller +# + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt + +# Button labeled as "BACK" (left-pointing triangle) +key 314 BUTTON_SELECT + +# The branded "X" button in the center of the controller +key 316 BUTTON_MODE + +# Button labeled as "START" (right-pointing triangle) +key 315 BUTTON_START diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f900.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f900.kl new file mode 100644 index 0000000000..44848ba0cf --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0e6f_Product_f900.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Afterglow AX.1 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000a.kl new file mode 100644 index 0000000000..b3aea049f6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori Co. DOA4 FightStick +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000c.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000c.kl new file mode 100644 index 0000000000..49c3addd78 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_000c.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori PadEX Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_0067.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_0067.kl new file mode 100644 index 0000000000..0dfccebd73 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_0067.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# HORIPAD ONE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_00c1.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_00c1.kl new file mode 100644 index 0000000000..c74512a3a4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_0f0d_Product_00c1.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Device name: HORI CO.,LTD. HORIPAD S +# HORIPAD for Nintendo Switch, USB game controller +# https://hori.co.uk/horipad-for-nintendo-switch/ + +# Button labeled as "Y" but should really produce keycode "X" +key 0x130 BUTTON_X +# Button labeled as "B" but should really produce keycode "A" +key 0x131 BUTTON_A +# Button labeled as "A" but should really produce keycode "B" +key 0x132 BUTTON_B +# Button labeled as "X" but should really product keycode "Y" +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Minus +key 0x138 BUTTON_SELECT +# Plus +key 0x139 BUTTON_START + +# Analog stick buttons +key 0x13a BUTTON_THUMBL +key 0x13b BUTTON_THUMBR + +# Home +key 0x13c HOME +# Capture +key 0x13d BUTTON_MODE + +# Left analog stick +axis 0x00 X +axis 0x01 Y +# Right analog stick +axis 0x02 Z +axis 0x05 RZ +# D-pad +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1412.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1412.kl new file mode 100644 index 0000000000..551b0bd756 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1412.kl @@ -0,0 +1,31 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Steelseries Free + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 316 BUTTON_SELECT + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1430.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1430.kl new file mode 100644 index 0000000000..e635c1d8fa --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1430.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# SteelSeries Stratus Duo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1431.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1431.kl new file mode 100644 index 0000000000..e635c1d8fa --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1431.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# SteelSeries Stratus Duo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1434.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1434.kl new file mode 100644 index 0000000000..8182edd69a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1038_Product_1434.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# SteelSeries Stratus+ +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_11c9_Product_55f0.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_11c9_Product_55f0.kl new file mode 100644 index 0000000000..dbb4a7e6ec --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_11c9_Product_55f0.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Nacon GC-100XF +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12ab_Product_0301.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12ab_Product_0301.kl new file mode 100644 index 0000000000..36956c1386 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12ab_Product_0301.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP AFTERGLOW AX.1 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12bd_Product_d015.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12bd_Product_d015.kl new file mode 100644 index 0000000000..557d62f135 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_12bd_Product_d015.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Hitgaming SNES Retro + +key 306 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 304 BUTTON_Y +key 308 BUTTON_L1 +key 309 BUTTON_R1 +key 313 BUTTON_START +key 312 BUTTON_SELECT + +axis 0x00 HAT_X +axis 0x01 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_4748.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_4748.kl new file mode 100644 index 0000000000..dbe83087ab --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_4748.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# RedOctane Guitar Hero X-plorer +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_f801.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_f801.kl new file mode 100644 index 0000000000..a8f9146203 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1430_Product_f801.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# RedOctane Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_146b_Product_0601.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_146b_Product_0601.kl new file mode 100644 index 0000000000..ea2f2211a1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_146b_Product_0601.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# BigBen Interactive XBOX 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0037.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0037.kl new file mode 100644 index 0000000000..39d8b2e6c1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0037.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Sabertooth +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0705.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0705.kl new file mode 100644 index 0000000000..611aaec1c2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0705.kl @@ -0,0 +1,64 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Mobile Controller with wired USB interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x133 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select key +key 0x13a BUTTON_SELECT +# Start key +key 0x13b BUTTON_START +# Home key +key 0xac BUTTON_MODE +# Back key +key 0x9e BACK diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0707.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0707.kl new file mode 100644 index 0000000000..48c171468b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0707.kl @@ -0,0 +1,64 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Mobile Controller with wireless Bluetooth interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x133 BUTTON_X +# Cross +key 0x130 BUTTON_A +# Circle +key 0x131 BUTTON_B +# Triangle +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select key +key 0x13a BUTTON_SELECT +# Start key +key 0x13b BUTTON_START +# Home key +key 0xac BUTTON_MODE +# Back key +key 0x9e BACK diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0709.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0709.kl new file mode 100644 index 0000000000..20ea2ab750 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0709.kl @@ -0,0 +1,51 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Junglecat Controller with wireless Bluetooth interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x133 BUTTON_X +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13d BUTTON_THUMBL +# Right stick click +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Select key +key 0x13a BUTTON_SELECT +# Start key +key 0x13b BUTTON_START diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0900.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0900.kl new file mode 100644 index 0000000000..4c6c4dda02 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0900.kl @@ -0,0 +1,48 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Razer Serval + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +key 158 BACK +key 172 HOME + +# Left arrow to the left of the "power" key +key 0x13a BUTTON_SELECT +# Right arrow to the right of the "power" key +key 0x13b BUTTON_START +# Power key +key 0x13c BUTTON_MODE + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +led 0x00 CONTROLLER_1 +led 0x01 CONTROLLER_2 +led 0x02 CONTROLLER_3 +led 0x03 CONTROLLER_4 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0a03.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0a03.kl new file mode 100644 index 0000000000..75775e9939 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_0a03.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Wildcat +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1004.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1004.kl new file mode 100644 index 0000000000..bfbfed5e9c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1004.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Ultimate Edition Controller with wired USB interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1007.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1007.kl new file mode 100644 index 0000000000..6f6c286b34 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1007.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Tournament Edition Controller with wired USB interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1009.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1009.kl new file mode 100644 index 0000000000..c380d5c335 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_1009.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Ultimate Edition Controller with wireless Bluetooth interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_100a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_100a.kl new file mode 100644 index 0000000000..b0e966d519 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1532_Product_100a.kl @@ -0,0 +1,65 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Raiju Tournament Edition Controller with wireless Bluetooth interface. +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# L2 axis +axis 0x09 RTRIGGER +# R2 axis +axis 0x0a LTRIGGER + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE + +# Touchpad press +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f00.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f00.kl new file mode 100644 index 0000000000..0d641cf9f9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f00.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Power A Mini Pro Elite +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f0a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f0a.kl new file mode 100644 index 0000000000..9e98aeec7b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f0a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox Airflo wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f10.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f10.kl new file mode 100644 index 0000000000..7fb0fea910 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_15e4_Product_3f10.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Batarang Xbox 360 controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_162e_Product_beef.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_162e_Product_beef.kl new file mode 100644 index 0000000000..e7fab5dba3 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_162e_Product_beef.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Joytech Neo-Se Take2 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd00.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd00.kl new file mode 100644 index 0000000000..8407b13064 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd00.kl @@ -0,0 +1,38 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Razer Onza Tournament Edition + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR +key 706 DPAD_UP +key 705 DPAD_RIGHT +key 707 DPAD_DOWN +key 704 DPAD_LEFT + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd01.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd01.kl new file mode 100644 index 0000000000..cacc07526b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fd01.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Razer Xbox 360 Gamepad + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fe00.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fe00.kl new file mode 100644 index 0000000000..467173f001 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1689_Product_fe00.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Razer Sabertooth Elite + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_0200.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_0200.kl new file mode 100644 index 0000000000..d30bcc60e6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_0200.kl @@ -0,0 +1,71 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Keyboard map for the android virtual remote running as a gamepad +# + +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x133 BUTTON_X +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 +key 0x138 BUTTON_L1 +key 0x139 BUTTON_R1 + +key 0x13a BUTTON_SELECT +key 0x13b BUTTON_START +key 0x13c BUTTON_MODE + +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +key 103 DPAD_UP +key 108 DPAD_DOWN +key 105 DPAD_LEFT +key 106 DPAD_RIGHT + +# Generic usage buttons +key 0x2c0 BUTTON_1 +key 0x2c1 BUTTON_2 +key 0x2c2 BUTTON_3 +key 0x2c3 BUTTON_4 +key 0x2c4 BUTTON_5 +key 0x2c5 BUTTON_6 +key 0x2c6 BUTTON_7 +key 0x2c7 BUTTON_8 +key 0x2c8 BUTTON_9 +key 0x2c9 BUTTON_10 +key 0x2ca BUTTON_11 +key 0x2cb BUTTON_12 +key 0x2cc BUTTON_13 +key 0x2cd BUTTON_14 +key 0x2ce BUTTON_15 +key 0x2cf BUTTON_16 + +# assistant buttons +key 0x246 VOICE_ASSIST +key 0x247 ASSIST + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_2c40.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_2c40.kl new file mode 100644 index 0000000000..2b42f871b0 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_2c40.kl @@ -0,0 +1,49 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Odie + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y + +led 0x00 CONTROLLER_1 +led 0x01 CONTROLLER_2 +led 0x02 CONTROLLER_3 +led 0x03 CONTROLLER_4 + +# The next 2 buttons do not follow Linux standard because this behaviour was specified by the UX +# Left arrow to the immediate left of the power button +key 158 BACK +# Circle to the immediate right of the power button +key 172 HOME + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Power button +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_4f80.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_4f80.kl new file mode 100644 index 0000000000..f6e4dc4091 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_4f80.kl @@ -0,0 +1,19 @@ +# Copyright (C) 2022 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Android Stylus +# + +key 0x242 STYLUS_BUTTON_TAIL diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_5018.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_5018.kl new file mode 100644 index 0000000000..e95ccb5c47 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_5018.kl @@ -0,0 +1,84 @@ +# Copyright (C) 2015 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Key layout for Google Pixel C Keyboard +# + +# Row 1 +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 14 DEL # Backspace + +# Row 2 +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 13 EQUALS +key 28 ENTER + +# Row 3 +key 125 META_LEFT # "Search key" +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE + +# Row 4 +key 42 SHIFT_LEFT +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT + +# Row 5 +key 29 CTRL_LEFT +key 56 ALT_LEFT +key 57 SPACE +key 100 ALT_RIGHT +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 108 DPAD_DOWN diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_9451.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_9451.kl new file mode 100644 index 0000000000..2a1f897b5e --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_18d1_Product_9451.kl @@ -0,0 +1,39 @@ +# Copyright 2023 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Key Layout file for flavor of Google Remote Control. +# + +key 116 POWER WAKE +key 217 ASSIST + +key 103 DPAD_UP +key 108 DPAD_DOWN +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 353 DPAD_CENTER WAKE + +key 158 BACK WAKE +key 172 HOME WAKE + +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP + +# custom keys +key usage 0x000c0186 MACRO_1 WAKE + +key usage 0x000c0077 BUTTON_3 WAKE #YouTube +key usage 0x000c0078 BUTTON_4 WAKE #Netflix diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1949_Product_0401.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1949_Product_0401.kl new file mode 100644 index 0000000000..ab24bcdeda --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1949_Product_0401.kl @@ -0,0 +1,27 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Amazon Fire TV remote + +key 103 DPAD_UP +key 108 DPAD_DOWN +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 96 DPAD_CENTER +key 158 BACK +key 172 HOME +key 168 MEDIA_REWIND +key 208 MEDIA_FAST_FORWARD +key 164 MEDIA_PLAY_PAUSE +key 217 ASSIST diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_0002.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_0002.kl new file mode 100644 index 0000000000..d8eaaba4a9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_0002.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Harmonix Rock Band Guitar +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f016.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f016.kl new file mode 100644 index 0000000000..00f8559150 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f016.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Madcatz Gamepad + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f021.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f021.kl new file mode 100644 index 0000000000..9fd688b0c6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f021.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Cats Ghost Recon FS GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f023.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f023.kl new file mode 100644 index 0000000000..175a523b5b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f023.kl @@ -0,0 +1,35 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Mad Catz MLG GamePad for Xbox 360 + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f025.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f025.kl new file mode 100644 index 0000000000..03aab446cf --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f025.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Mad Catz Call Of Duty +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f027.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f027.kl new file mode 100644 index 0000000000..216e3daebb --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f027.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MadCatz FPS Pro + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f028.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f028.kl new file mode 100644 index 0000000000..51733313d9 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f028.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Street Fighter IV FightPad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f036.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f036.kl new file mode 100644 index 0000000000..f27de1c0e4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f036.kl @@ -0,0 +1,36 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# MadCatz Generic XBox Controller + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BACK +key 316 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f038.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f038.kl new file mode 100644 index 0000000000..79e147d06d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f038.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Street Fighter IV FightStick TE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f501.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f501.kl new file mode 100644 index 0000000000..1282532ab5 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f501.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# HoriPad EX2 Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f506.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f506.kl new file mode 100644 index 0000000000..3a9d4620ba --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f506.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori Real Arcade Pro.EX Premium VLX +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f900.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f900.kl new file mode 100644 index 0000000000..9cfceb433a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f900.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Harmonix Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f901.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f901.kl new file mode 100644 index 0000000000..86d45e58ad --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f901.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Gamestop Xbox 360 Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f903.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f903.kl new file mode 100644 index 0000000000..f61c050059 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f903.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Tron Xbox 360 controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f904.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f904.kl new file mode 100644 index 0000000000..3e02a24f9e --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_f904.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PDP Versus Fighting Pad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fa01.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fa01.kl new file mode 100644 index 0000000000..517413d2ed --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fa01.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# MadCatz GamePad +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd00.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd00.kl new file mode 100644 index 0000000000..fc6a4f8533 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd00.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Onza TE +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd01.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd01.kl new file mode 100644 index 0000000000..8882abf0f7 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1bad_Product_fd01.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Onza +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1d79_Product_0009.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1d79_Product_0009.kl new file mode 100644 index 0000000000..a3d5bbdb3c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_1d79_Product_0009.kl @@ -0,0 +1,40 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# Nyko Playpad / Playpad Pro + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 158 BACK +key 172 HOME +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 108 DPAD_DOWN + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x05 RZ +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_22b8_Product_093d.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_22b8_Product_093d.kl new file mode 100644 index 0000000000..2749c5ba98 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_22b8_Product_093d.kl @@ -0,0 +1,105 @@ +# Copyright (C) 2010 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Motorola Bluetooth Wireless Keyboard. +# + +key 1 BACK +key 2 1 +key 3 2 +key 4 3 +key 5 4 +key 6 5 +key 7 6 +key 8 7 +key 9 8 +key 10 9 +key 11 0 +key 12 MINUS +key 13 EQUALS +key 14 DEL +key 15 TAB +key 16 Q +key 17 W +key 18 E +key 19 R +key 20 T +key 21 Y +key 22 U +key 23 I +key 24 O +key 25 P +key 26 LEFT_BRACKET +key 27 RIGHT_BRACKET +key 28 ENTER +key 29 CTRL_LEFT +key 30 A +key 31 S +key 32 D +key 33 F +key 34 G +key 35 H +key 36 J +key 37 K +key 38 L +key 39 SEMICOLON +key 40 APOSTROPHE +key 41 GRAVE +key 42 SHIFT_LEFT +key 43 BACKSLASH +key 44 Z +key 45 X +key 46 C +key 47 V +key 48 B +key 49 N +key 50 M +key 51 COMMA +key 52 PERIOD +key 53 SLASH +key 54 SHIFT_RIGHT +key 56 ALT_LEFT +key 57 SPACE +key 58 CAPS_LOCK +key 59 F1 +key 60 F2 +key 61 F3 +key 62 F4 +key 63 F5 +key 64 F6 +key 65 F7 +key 66 F8 +key 67 F9 +key 68 F10 +key 87 F11 +key 88 F12 +key 97 CTRL_RIGHT +key 102 HOME +key 103 DPAD_UP +key 105 DPAD_LEFT +key 106 DPAD_RIGHT +key 107 MOVE_END +key 108 DPAD_DOWN +key 111 FORWARD_DEL +key 113 VOLUME_MUTE +key 114 VOLUME_DOWN +key 115 VOLUME_UP +key 125 MENU +key 127 SEARCH +key 163 MEDIA_NEXT +key 164 MEDIA_PLAY_PAUSE +key 165 MEDIA_PREVIOUS +key 166 MEDIA_STOP +# key 226 tbd reserved key diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_1008.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_1008.kl new file mode 100644 index 0000000000..7b19469ab6 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_1008.kl @@ -0,0 +1,40 @@ +# Copyright (C) 2014 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# OnLive, Inc. OnLive Wireless Controller, USB adapter + +key 164 MEDIA_PLAY_PAUSE +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 208 MEDIA_FAST_FORWARD +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_SELECT +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_100a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_100a.kl new file mode 100644 index 0000000000..cb2b73afee --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2378_Product_100a.kl @@ -0,0 +1,40 @@ +# Copyright (C) 2013 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# OnLive, Inc. OnLive Wireless Controller + +key 164 MEDIA_PLAY_PAUSE +key 167 MEDIA_RECORD +key 168 MEDIA_REWIND +key 208 MEDIA_FAST_FORWARD +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 315 BUTTON_START +key 314 BUTTON_SELECT +key 316 BUTTON_MODE +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +axis 0x00 X +axis 0x01 Y +axis 0x03 Z +axis 0x04 RZ +axis 0x05 RTRIGGER +axis 0x02 LTRIGGER +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5300.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5300.kl new file mode 100644 index 0000000000..303e906ad7 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5300.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA MINI PROEX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5303.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5303.kl new file mode 100644 index 0000000000..9e98aeec7b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5303.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox Airflo wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_530a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_530a.kl new file mode 100644 index 0000000000..aa88515c08 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_530a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox 360 Pro EX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_531a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_531a.kl new file mode 100644 index 0000000000..09a5c6a85c --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_531a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA Pro Ex +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5397.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5397.kl new file mode 100644 index 0000000000..66b896a36d --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5397.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# FUS1ON Tournament Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_541a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_541a.kl new file mode 100644 index 0000000000..24271fbbba --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_541a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA Xbox One Mini Wired Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_542a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_542a.kl new file mode 100644 index 0000000000..623bd1375b --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_542a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Xbox ONE spectra +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_543a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_543a.kl new file mode 100644 index 0000000000..59769c4c05 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_543a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA Xbox One wired controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5500.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5500.kl new file mode 100644 index 0000000000..d76d7d0826 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5500.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori XBOX 360 EX 2 with Turbo +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5501.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5501.kl new file mode 100644 index 0000000000..64d901af13 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5501.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori Real Arcade Pro VX-SA +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5506.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5506.kl new file mode 100644 index 0000000000..bfb23c3090 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5506.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori SOULCALIBUR V Stick +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_550d.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_550d.kl new file mode 100644 index 0000000000..24852b0bd8 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_550d.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Hori GEM Xbox controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_551a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_551a.kl new file mode 100644 index 0000000000..5e338a5c22 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_551a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA FUSION Pro Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_561a.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_561a.kl new file mode 100644 index 0000000000..57b7ddcca2 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_561a.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# PowerA FUSION Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5b02.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5b02.kl new file mode 100644 index 0000000000..bcf354d0e4 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5b02.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Thrustmaster, Inc. GPX Controller +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5d04.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5d04.kl new file mode 100644 index 0000000000..39d8b2e6c1 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_5d04.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Sabertooth +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_fafe.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_fafe.kl new file mode 100644 index 0000000000..f8d3f0c080 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_24c6_Product_fafe.kl @@ -0,0 +1,58 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Rock Candy Gamepad for Xbox 360 +# Autogenerated based on Vendor_045e_Product_02ea.kl (XBox One Controller - Model 1708) +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 + +# Triggers. +axis 0x02 LTRIGGER +axis 0x05 RTRIGGER + +# Left and right stick. +# The reported value for flat is 128 out of a range from -32767 to 32768, which is absurd. +# This confuses applications that rely on the flat value because the joystick actually +# settles in a flat range of +/- 4096 or so. +axis 0x00 X flat 4096 +axis 0x01 Y flat 4096 +axis 0x03 Z flat 4096 +axis 0x04 RZ flat 4096 + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Two overlapping rectangles +key 314 BUTTON_SELECT +# Hamburger - 3 parallel lines +key 315 BUTTON_START + +# Xbox key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbe.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbe.kl new file mode 100644 index 0000000000..211e532c83 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbe.kl @@ -0,0 +1,54 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Gamevice GV186 Mobile Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x133 BUTTON_X +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +axis 0x00 X +axis 0x01 Y + +axis 0x02 Z +axis 0x05 RZ + +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER + +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Left Triangle Button +key 0x13a BUTTON_SELECT +# Right Triangle Button +key 0x13b BUTTON_START +# Home Button +key 0x13c BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbf.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbf.kl new file mode 100644 index 0000000000..a59f566384 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_27f8_Product_0bbf.kl @@ -0,0 +1,54 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Razer Kishi Mobile Controller +# + + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 0x133 BUTTON_X +key 0x130 BUTTON_A +key 0x131 BUTTON_B +key 0x134 BUTTON_Y + +key 0x136 BUTTON_L1 +key 0x137 BUTTON_R1 +key 0x138 BUTTON_L2 +key 0x139 BUTTON_R2 + +axis 0x00 X +axis 0x01 Y + +axis 0x02 Z +axis 0x05 RZ + +axis 0x09 RTRIGGER +axis 0x0a LTRIGGER + +key 0x13d BUTTON_THUMBL +key 0x13e BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Left Triangle Button +key 0x13a BUTTON_SELECT +# Right Triangle Button +key 0x13b BUTTON_START +# Home Button +key 0x13c BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_28de_Product_1102.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_28de_Product_1102.kl new file mode 100644 index 0000000000..150a17a782 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_28de_Product_1102.kl @@ -0,0 +1,74 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Steam Controller - Model 1001 - USB +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +key 304 BUTTON_A +key 305 BUTTON_B +key 307 BUTTON_X +key 308 BUTTON_Y + +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 + +# Triggers. +axis 0x15 LTRIGGER +axis 0x14 RTRIGGER + +# Left and right stick. +axis 0x00 X +axis 0x01 Y + +# Right stick / mousepad +axis 0x03 Z +axis 0x04 RZ + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Hat. +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Dpad (clicks) +key 544 DPAD_UP +key 545 DPAD_DOWN +key 546 DPAD_LEFT +key 547 DPAD_RIGHT + +# Touching the dpad (light touch without pressing down) +key 289 BUTTON_1 +# Touching the "right stick" / mousepad (light touch without pressing down) +key 290 BUTTON_2 + +# Pressing the large paddle on the back, left (linux BTN_WHEEL / BTN_GEAR_DOWN) +key 336 BUTTON_3 +# Pressing the large paddle on the back, right (linux BTN_GEAR_UP) +key 337 BUTTON_4 + + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Left arrow +key 314 BUTTON_SELECT +# Right arrow +key 315 BUTTON_START + +# Steam key +key 316 BUTTON_MODE diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2dc8_Product_6101.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2dc8_Product_6101.kl new file mode 100644 index 0000000000..ec9f5581b0 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2dc8_Product_6101.kl @@ -0,0 +1,55 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# 8BitDo - SN30 Pro gamepad in Android (D-Input) mode +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Button labeled as "A" but should really produce keycode "B" +key 304 BUTTON_B +# Button labeled as "B" but should really produce keycode "A" +key 305 BUTTON_A +# Button labeled as "X" but should really produce keycode "Y" +key 307 BUTTON_Y +# Button labeled as "Y" but should really produce keycode "X" +key 308 BUTTON_X + +key 310 BUTTON_L1 +key 312 BUTTON_L2 +key 311 BUTTON_R1 +key 313 BUTTON_R2 + +# Button "Start" does not emit event when gamepad is in Android mode +# Button "Home" +key 306 BUTTON_MODE +key 314 BUTTON_SELECT +key 315 BUTTON_START + +key 317 BUTTON_THUMBL +key 318 BUTTON_THUMBR + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y + +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Dpad +axis 0x10 HAT_X +axis 0x11 HAT_Y + diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2e95_Product_7725.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2e95_Product_7725.kl new file mode 100644 index 0000000000..7672e22f8a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/Vendor_2e95_Product_7725.kl @@ -0,0 +1,64 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# +# Scuf Vantage Controller +# + +# Mapping according to https://developer.android.com/training/game-controllers/controller-input.html + +# Square +key 0x130 BUTTON_X +# Cross +key 0x131 BUTTON_A +# Circle +key 0x132 BUTTON_B +# Triangle +key 0x133 BUTTON_Y + +key 0x134 BUTTON_L1 +key 0x135 BUTTON_R1 +key 0x136 BUTTON_L2 +key 0x137 BUTTON_R2 + +# L2 Trigger axis +axis 0x03 LTRIGGER +# R2 Trigger axis +axis 0x04 RTRIGGER + +# Left Analog Stick +axis 0x00 X +axis 0x01 Y +# Right Analog Stick +axis 0x02 Z +axis 0x05 RZ + +# Left stick click +key 0x13a BUTTON_THUMBL +# Right stick click +key 0x13b BUTTON_THUMBR + +# Hat +axis 0x10 HAT_X +axis 0x11 HAT_Y + +# Mapping according to https://www.kernel.org/doc/Documentation/input/gamepad.txt +# Share +key 0x138 BUTTON_SELECT +# Options +key 0x139 BUTTON_START +# PS key +key 0x13c BUTTON_MODE +# Touchpad press +key 0x13d BUTTON_1 diff --git a/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/idroid_con.kl b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/idroid_con.kl new file mode 100644 index 0000000000..6d4a163d9f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/test_data/pixel-7/idroid_con.kl @@ -0,0 +1,37 @@ +# Copyright (C) 2020 The Android Open Source Project +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +# SnakeByte iDroid:con + +key 304 BUTTON_A +key 305 BUTTON_B +key 306 BUTTON_THUMBL +key 307 BUTTON_X +key 308 BUTTON_Y +key 309 BUTTON_THUMBR +key 310 BUTTON_L1 +key 311 BUTTON_R1 +key 312 BUTTON_L2 +key 313 BUTTON_R2 +key 314 BUTTON_SELECT +key 315 BUTTON_START + +axis 0x00 X +axis 0x01 Y +axis 0x02 Z +axis 0x03 RX +axis 0x04 RY +axis 0x05 RZ +axis 0x10 HAT_X +axis 0x11 HAT_Y diff --git a/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs b/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs new file mode 100644 index 0000000000..09a7ab6360 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/core/tests/tokenizer_test.rs @@ -0,0 +1,145 @@ +use evdev_manager_core::android::keylayout::tokenizer::Tokenizer; +#[cfg(test)] +use pretty_assertions::assert_eq; +use std::path::PathBuf; + +#[test] +fn test_is_eof() { + let tokenizer = Tokenizer::from_contents(PathBuf::new(), ""); + assert!(tokenizer.is_eof()); + + let tokenizer = Tokenizer::from_contents(PathBuf::new(), "a"); + assert!(!tokenizer.is_eof()); +} + +#[test] +fn test_is_eol() { + let tokenizer = Tokenizer::from_contents(PathBuf::new(), ""); + assert!(tokenizer.is_eol()); + + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "a\nb"); + assert!(!tokenizer.is_eol()); + tokenizer.next_char(); + assert!(tokenizer.is_eol()); +} + +#[test] +fn test_peek_char() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "abc"); + assert_eq!(tokenizer.peek_char(), 'a'); + assert_eq!(tokenizer.peek_char(), 'a'); // Should not advance + + tokenizer.next_char(); + assert_eq!(tokenizer.peek_char(), 'b'); +} + +#[test] +fn test_next_char() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "abc"); + assert_eq!(tokenizer.next_char(), 'a'); + assert_eq!(tokenizer.next_char(), 'b'); + assert_eq!(tokenizer.next_char(), 'c'); + assert_eq!(tokenizer.next_char(), '\0'); +} + +#[test] +fn test_next_token() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "key 1 ESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), ""); +} + +#[test] +fn test_next_token_with_multiple_delimiters() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "key\t1\nESCAPE"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + tokenizer.next_line(); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); +} + +#[test] +fn test_next_line() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "line1\nline2\nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line2"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_skip_delimiters() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), " key"); + tokenizer.skip_delimiters(" \t"); + assert_eq!(tokenizer.peek_char(), 'k'); +} + +#[test] +fn test_get_location() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new().join("test.txt"), "line1\nline2"); + assert_eq!(tokenizer.get_location(), "test.txt:1"); + tokenizer.next_line(); + assert_eq!(tokenizer.get_location(), "test.txt:2"); +} + +#[test] +fn test_peek_remainder_of_line() { + let tokenizer = Tokenizer::from_contents(PathBuf::new(), "key 1 ESCAPE\nnext line"); + assert_eq!(tokenizer.peek_remainder_of_line(), "key 1 ESCAPE"); + + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "key 1 ESCAPE\nnext line"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "next line"); +} + +#[test] +fn test_handles_comments() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "key 1 ESCAPE # comment"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "ESCAPE"); + assert_eq!(tokenizer.peek_char(), ' '); + tokenizer.skip_delimiters(" \t"); + assert_eq!(tokenizer.peek_char(), '#'); +} + +#[test] +fn test_handles_empty_lines() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "line1\n\nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), ""); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_handles_whitespace_only_lines() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "line1\n \nline3"); + assert_eq!(tokenizer.peek_remainder_of_line(), "line1"); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), " "); + tokenizer.next_line(); + assert_eq!(tokenizer.peek_remainder_of_line(), "line3"); +} + +#[test] +fn test_multiple_lines_sequential() { + let mut tokenizer = Tokenizer::from_contents(PathBuf::new(), "key 1 A\nkey 2 B\nkey 3 C"); + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "1"); + assert_eq!(tokenizer.next_token(" \t"), "A"); + tokenizer.next_line(); + + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "2"); + assert_eq!(tokenizer.next_token(" \t"), "B"); + tokenizer.next_line(); + + assert_eq!(tokenizer.next_token(" \t"), "key"); + assert_eq!(tokenizer.next_token(" \t"), "3"); + assert_eq!(tokenizer.next_token(" \t"), "C"); +} diff --git a/evdev/src/main/rust/evdev_manager/jni/Cargo.toml b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml new file mode 100644 index 0000000000..932490aa99 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/Cargo.toml @@ -0,0 +1,15 @@ +[package] +name = "evdev_manager_jni" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager_jni" +crate-type = ["rlib"] + +[dependencies] +jni = "0.21.1" +log = "0.4.28" +evdev = { path = "../../evdev" } +evdev_manager_core = { path = "../core" } +libc = "0.2.177" diff --git a/evdev/src/main/rust/evdev_manager/jni/build.rs b/evdev/src/main/rust/evdev_manager/jni/build.rs new file mode 100644 index 0000000000..be1f4d1098 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/build.rs @@ -0,0 +1,16 @@ +fn main() { + // This crate now uses pure JNI without C++ Binder layer. + // No C++ compilation needed. + + // Just check that we're building for Android + let target = std::env::var("TARGET").expect("TARGET environment variable not set"); + let is_android = target.contains("android"); + + if !is_android { + eprintln!( + "Warning: Building for non-Android target '{}'. This crate is designed for Android.", + target + ); + eprintln!("Use Gradle for actual builds."); + } +} diff --git a/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs new file mode 100644 index 0000000000..5bf31d541f --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/evdev_jni_observer.rs @@ -0,0 +1,280 @@ +use evdev::{util::event_code_to_int, InputEvent}; +use evdev_manager_core::android::android_codes; +use evdev_manager_core::android::android_codes::AKEYCODE_UNKNOWN; +use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; +use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; +use evdev_manager_core::grabbed_device_handle::GrabbedDeviceHandle; +use jni::objects::{GlobalRef, JValue}; +use jni::JavaVM; +use std::process; +use std::sync::{Arc, Mutex}; + +pub struct EvdevJniObserver { + jvm: Arc, + system_bridge: GlobalRef, + key_layout_map_manager: Arc, + power_button_down_time: Mutex, +} + +impl std::fmt::Debug for EvdevJniObserver { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.debug_struct("EvdevJniObserver") + .field("jvm", &"") + .field("system_bridge", &"") + .field("key_layout_map_manager", &"") + .finish() + } +} + +/// Observer that forwards events to BaseSystemBridge via JNI +/// Performs KeyLayoutMap conversion from raw evdev codes to Android keycodes +impl EvdevJniObserver { + pub fn new( + jvm: Arc, + system_bridge: GlobalRef, + key_layout_map_manager: Arc, + ) -> Self { + EvdevJniObserver { + jvm, + system_bridge, + key_layout_map_manager, + power_button_down_time: Mutex::new(0), + } + } + + /// Handle power button emergency kill. + fn handle_power_button( + &self, + ev_code: u32, + android_code: u32, + value: i32, + time_sec: libc::time_t, + ) { + let mut time_guard = self.power_button_down_time.lock().unwrap(); + // KEY_POWER scan code = 116 + if ev_code == 116 || android_code == android_codes::AKEYCODE_POWER { + if value == 1 { + *time_guard = time_sec; + } else if value == 0 { + // Button up - check if held for 10+ seconds + let down_time = *time_guard; + if down_time > 0 && time_sec - down_time >= 10 { + // Call BaseSystemBridge.onEmergencyKillSystemBridge() via JNI + if let Ok(mut env) = self.jvm.attach_current_thread() { + let _ = env.call_method( + &self.system_bridge, + "onEmergencyKillSystemBridge", + "()V", + &[], + ); + } + process::exit(0); + } + *time_guard = 0 + } + } + } + + pub fn on_grabbed_devices_changed(&self, grabbed_devices: Vec) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Convert Vec to Java array of GrabbedDeviceHandle + let handle_class = + match env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle") { + Ok(c) => c, + Err(e) => { + error!("Failed to find GrabbedDeviceHandle class: {:?}", e); + return; + } + }; + + let array = match env.new_object_array( + grabbed_devices.len() as i32, + &handle_class, + jni::objects::JObject::null(), + ) { + Ok(a) => a, + Err(e) => { + error!("Failed to create GrabbedDeviceHandle array: {:?}", e); + return; + } + }; + + for (i, device_handle) in grabbed_devices.iter().enumerate() { + let name_str = match env.new_string(&device_handle.device_info.name) { + Ok(s) => s, + Err(e) => { + error!("Failed to create device name string: {:?}", e); + continue; + } + }; + + let handle = match env.new_object( + &handle_class, + "(ILjava/lang/String;III)V", + &[ + JValue::Int(device_handle.id as i32), + JValue::Object(&name_str.into()), + JValue::Int(device_handle.device_info.bus as i32), + JValue::Int(device_handle.device_info.vendor as i32), + JValue::Int(device_handle.device_info.product as i32), + ], + ) { + Ok(h) => h, + Err(e) => { + error!("Failed to create GrabbedDeviceHandle: {:?}", e); + continue; + } + }; + + if let Err(e) = env.set_object_array_element(&array, i as i32, &handle) { + error!("Failed to set array element: {:?}", e); + } + } + + // Call SystemBridge.onGrabbedDevicesChanged() via JNI + if let Err(e) = env.call_method( + &self.system_bridge, + "onGrabbedDevicesChanged", + "([Lio/github/sds100/keymapper/common/models/GrabbedDeviceHandle;)V", + &[JValue::Object(&array.into())], + ) { + error!("Failed to call onGrabbedDevicesChanged: {:?}", e); + } + } + + pub fn on_event( + &self, + device_id: usize, + device_identifier: &EvdevDeviceInfo, + event: &InputEvent, + ) -> bool { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Extract event type and code from EventCode + let (ev_type, ev_code) = event_code_to_int(&event.event_code); + + let key_result = self + .key_layout_map_manager + .map_key(device_identifier, ev_code); + + // Convert raw evdev code to Android keycode + let android_code = match key_result { + Ok(Some(key_code)) => key_code, + Ok(None) | Err(_) => AKEYCODE_UNKNOWN, + }; + + // Handle power button emergency kill + self.handle_power_button(ev_code, android_code, event.value, event.time.tv_sec); + + // Call BaseSystemBridge.onEvdevEvent() via JNI + + let result = env.call_method( + &self.system_bridge, + "onEvdevEvent", + "(IJJIIII)Z", + &[ + JValue::Int(device_id as i32), + #[allow(clippy::unnecessary_cast)] + // When building for 32 bit the tv_sec type may be i32 + JValue::Long(event.time.tv_sec as i64), + JValue::Long(event.time.tv_usec.into()), + JValue::Int(ev_type as i32), + JValue::Int(ev_code as i32), + JValue::Int(event.value), + JValue::Int(android_code as i32), + ], + ); + + match result { + Ok(value) => { + // The method returns a primitive boolean (Z) + // Extract the boolean value using z() method which returns Result + value.z().unwrap_or_else(|e| { + error!("Failed to extract boolean from result: {:?}", e); + false + }) + } + Err(e) => { + error!("Failed to call onEvdevEvent: {:?}", e); + false + } + } + } + + pub fn on_evdev_devices_changed(&self, devices: Vec) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + // Convert Vec to Java array of EvdevDeviceInfo + let info_class = + match env.find_class("io/github/sds100/keymapper/common/models/EvdevDeviceInfo") { + Ok(c) => c, + Err(e) => { + error!("Failed to find EvdevDeviceInfo class: {:?}", e); + return; + } + }; + + let array = match env.new_object_array( + devices.len() as i32, + &info_class, + jni::objects::JObject::null(), + ) { + Ok(a) => a, + Err(e) => { + error!("Failed to create EvdevDeviceInfo array: {:?}", e); + return; + } + }; + + for (i, device_info) in devices.iter().enumerate() { + let name_str = match env.new_string(&device_info.name) { + Ok(s) => s, + Err(e) => { + error!("Failed to create device name string: {:?}", e); + continue; + } + }; + + let info = match env.new_object( + &info_class, + "(Ljava/lang/String;III)V", + &[ + JValue::Object(&name_str.into()), + JValue::Int(device_info.bus as i32), + JValue::Int(device_info.vendor as i32), + JValue::Int(device_info.product as i32), + ], + ) { + Ok(i) => i, + Err(e) => { + error!("Failed to create EvdevDeviceInfo: {:?}", e); + continue; + } + }; + + if let Err(e) = env.set_object_array_element(&array, i as i32, &info) { + error!("Failed to set array element: {:?}", e); + } + } + + // Call SystemBridge.onEvdevDevicesChanged() via JNI + if let Err(e) = env.call_method( + &self.system_bridge, + "onEvdevDevicesChanged", + "([Lio/github/sds100/keymapper/common/models/EvdevDeviceInfo;)V", + &[JValue::Object(&array.into())], + ) { + error!("Failed to call onEvdevDevicesChanged: {:?}", e); + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs new file mode 100644 index 0000000000..9e9cf4b53a --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/jni_bridge.rs @@ -0,0 +1,388 @@ +use crate::evdev_jni_observer::EvdevJniObserver; +use crate::logging::{AndroidLogLevel, KeyMapperLogger}; +use evdev::InputEvent; +use evdev_manager_core::android::keylayout::key_layout_map_manager::KeyLayoutMapManager; +use evdev_manager_core::evdev_device_info::EvdevDeviceInfo; +use evdev_manager_core::event_loop::{EvdevCallback, EventLoopManager}; +use evdev_manager_core::grab_target_key_code::GrabTargetKeyCode; +use evdev_manager_core::grabbed_device_handle::GrabbedDeviceHandle; +use jni::objects::{JClass, JIntArray, JObject, JObjectArray, JString, JValue}; +use jni::sys::{jboolean, jint, jobject, jobjectArray}; +use jni::JNIEnv; +use std::ffi::CString; +use std::ptr; +use std::sync::{Arc, OnceLock}; + +static JNI_OBSERVER: OnceLock = OnceLock::new(); + +fn get_jni_observer() -> &'static EvdevJniObserver { + JNI_OBSERVER.get().expect("JNI observer not initialized") +} + +/// Wrapper struct that implements EvdevCallback trait +struct JniEvdevCallback; + +impl EvdevCallback for JniEvdevCallback { + fn on_evdev_event( + &self, + device_id: usize, + device_identifier: &EvdevDeviceInfo, + event: &InputEvent, + ) -> bool { + get_jni_observer().on_event(device_id, device_identifier, event) + } + + fn on_grabbed_devices_changed(&self, grabbed_devices: Vec) { + get_jni_observer().on_grabbed_devices_changed(grabbed_devices) + } + + fn on_evdev_devices_changed(&self, devices: Vec) { + get_jni_observer().on_evdev_devices_changed(devices) + } +} + +/// MUST only be called once in the lifetime of the process. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_initEvdevManager( + env: JNIEnv, + this: JObject, +) { + // Get the JavaVM + let jvm = env.get_java_vm().expect("Failed to get JavaVM"); + let jvm_arc = Arc::new(jvm); + + // Create a global reference to the SystemBridge instance for logging + let system_bridge_for_log = env + .new_global_ref(&this) + .expect("Failed to create global reference to SystemBridge for logging"); + + // Initialize logging to Key Mapper and Android logcat. + KeyMapperLogger::init( + jvm_arc.clone(), + system_bridge_for_log, + CString::new("KeyMapperSystemBridge").unwrap(), + ); + + KeyMapperLogger::set_log_panic_hook(); + + info!("Initializing evdev manager"); + + // Create a global reference to the SystemBridge instance for evdev observer + let system_bridge = env + .new_global_ref(this) + .expect("Failed to create global reference to SystemBridge"); + + // Initialize the JNI observer + let key_layout_manager = KeyLayoutMapManager::get(); + let observer = EvdevJniObserver::new(jvm_arc, system_bridge, key_layout_manager); + + if JNI_OBSERVER.set(observer).is_err() { + panic!("JNI observer already initialized"); + } + + // Initialize and start the event loop with the callback + EventLoopManager::init(Arc::new(JniEvdevCallback)); + + EventLoopManager::get() + .start() + .inspect_err(|e| error!("Failed to start event loop: {:?}", e)) + .unwrap(); +} + +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_destroyEvdevManager( + _env: JNIEnv, + _class: JClass, +) { + info!("Destroying evdev manager"); + + EventLoopManager::get() + .stop() + .inspect_err(|e| error!("Failed to stop event loop: {:?}", e)) + .unwrap(); +} + +/// Set the log level from Kotlin. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setLogLevelNative( + _env: JNIEnv, + _class: JClass, + level: jint, +) { + KeyMapperLogger::set_level(AndroidLogLevel::from(level as i32)); +} + +/// Set the list of grabbed devices. Takes an array of GrabTargetKeyCode and returns an array of GrabbedDeviceHandle. +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_setGrabTargetsNative( + mut env: JNIEnv, + _class: JClass, + j_devices: jobjectArray, +) -> jobjectArray { + // Parse the input array of GrabTargetKeyCode + let mut requested_devices: Vec = Vec::new(); + + // Convert raw jobjectArray to JObjectArray + let devices_array: JObjectArray = unsafe { JObjectArray::from_raw(j_devices) }; + + let array_length = match env.get_array_length(&devices_array) { + Ok(len) => len, + Err(e) => { + error!("Failed to get array length: {:?}", e); + return ptr::null_mut(); + } + }; + + for i in 0..array_length { + let obj = match env.get_object_array_element(&devices_array, i) { + Ok(o) => o, + Err(e) => { + error!("Failed to get array element {}: {:?}", i, e); + continue; + } + }; + + match parse_grab_target_key_code(&mut env, &obj) { + Ok(grab_request) => requested_devices.push(grab_request), + Err(e) => { + error!("Failed to parse GrabTargetKeyCode at index {}: {:?}", i, e); + } + } + } + + let grabbed_devices = EventLoopManager::get().set_grab_targets(requested_devices); + create_java_grabbed_device_handle_array(&mut env, grabbed_devices) +} + +/// Write an event to a grabbed device using its device ID +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventNative( + _env: JNIEnv, + _class: JClass, + j_device_id: jint, + j_type: jint, + j_code: jint, + j_value: jint, +) -> jboolean { + EventLoopManager::get() + .write_event(j_device_id as usize, j_type as u32, j_code as u32, j_value) + .is_ok() as jboolean +} + +/// Write an event to a grabbed device using its device ID +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventKeyCodeNative( + _env: JNIEnv, + _class: JClass, + j_device_id: jint, + j_key_code: jint, + j_value: jint, +) -> jboolean { + EventLoopManager::get() + .write_key_code_event(j_device_id as usize, j_key_code as u32, j_value) + .is_ok() as jboolean +} + +/// Get all available evdev devices (returns EvdevDeviceInfo array) +#[no_mangle] +pub extern "system" fn Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_getEvdevDevicesNative( + mut env: JNIEnv, + _class: JClass, +) -> jobjectArray { + let mut device_infos = Vec::new(); + let devices_result = EventLoopManager::get().get_real_devices(); + + match devices_result { + Ok(devices) => { + for device in devices { + // Create EvdevDeviceInfo + match create_java_evdev_device_info( + &mut env, + &device.name, + device.bus as i32, + device.vendor as i32, + device.product as i32, + ) { + Ok(info) => device_infos.push(info), + Err(e) => { + error!("Failed to create EvdevDeviceInfo: {:?}", e); + } + } + } + } + Err(e) => { + error!("Failed to get input devices: {:?}", e); + } + } + + // Create Java array + let class = match env.find_class("io/github/sds100/keymapper/common/models/EvdevDeviceInfo") { + Ok(c) => c, + Err(e) => { + error!("Failed to find EvdevDeviceInfo class: {:?}", e); + return ptr::null_mut(); + } + }; + + let array = match env.new_object_array(device_infos.len() as i32, class, JObject::null()) { + Ok(a) => a, + Err(e) => { + error!("Failed to create EvdevDeviceInfo array: {:?}", e); + return ptr::null_mut(); + } + }; + + // Fill array + for (i, info) in device_infos.iter().enumerate() { + if let Err(e) = + env.set_object_array_element(&array, i as i32, unsafe { JObject::from_raw(*info) }) + { + error!("Failed to set array element: {:?}", e); + } + } + + array.into_raw() +} + +/// Parse a Java GrabTargetKeyCode object into a Rust GrabTargetKeyCode +fn parse_grab_target_key_code( + env: &mut JNIEnv, + obj: &JObject, +) -> Result { + // Get name field + let name_field = env.get_field(obj, "name", "Ljava/lang/String;")?; + let name_obj = name_field.l()?; + let name_jstring = JString::from(name_obj); + let name: String = env + .get_string(&name_jstring)? + .to_string_lossy() + .into_owned(); + + // Get bus field + let bus = env.get_field(obj, "bus", "I")?.i()?; + + // Get vendor field + let vendor = env.get_field(obj, "vendor", "I")?.i()?; + + // Get product field + let product = env.get_field(obj, "product", "I")?.i()?; + + // Get the extraKeyCodes field (int[]) + let extra_codes_field = env.get_field(obj, "extraKeyCodes", "[I")?; + let extra_codes_obj = extra_codes_field.l()?; + let extra_codes_array = JIntArray::from(extra_codes_obj); + + // Convert Java int[] to Vec + let array_length = env.get_array_length(&extra_codes_array)? as usize; + let mut extra_key_codes: Vec = Vec::with_capacity(array_length); + let mut buffer = vec![0i32; array_length]; + env.get_int_array_region(&extra_codes_array, 0, &mut buffer)?; + extra_key_codes.extend(buffer.iter().map(|&v| v as u32)); + + Ok(GrabTargetKeyCode { + name, + bus: bus as u16, + vendor: vendor as u16, + product: product as u16, + extra_key_codes, + }) +} + +fn create_java_grabbed_device_handle_array( + env: &mut JNIEnv, + grabbed_devices: Vec, +) -> jobjectArray { + let handle_class = + match env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle") { + Ok(c) => c, + Err(e) => { + panic!("Failed to find GrabbedDeviceHandle class: {:?}", e); + } + }; + + let array = + match env.new_object_array(grabbed_devices.len() as i32, &handle_class, JObject::null()) { + Ok(a) => a, + Err(e) => { + panic!("Failed to create GrabbedDeviceHandle array: {:?}", e); + } + }; + + // grabbed_devices contains (slab_key, DeviceIdentifier) tuples + // The slab_key is used as the device_id for O(1) lookup when writing events + for (i, handle) in grabbed_devices.iter().enumerate() { + match create_java_grabbed_device_handle( + env, + handle.id as i32, // slab key = device_id for O(1) lookup + &handle.device_info.name, + handle.device_info.bus as i32, + handle.device_info.vendor as i32, + handle.device_info.product as i32, + ) { + Ok(handle) => { + if let Err(e) = env.set_object_array_element(&array, i as i32, unsafe { + JObject::from_raw(handle) + }) { + error!("Failed to set array element: {:?}", e); + } + } + Err(e) => { + error!("Failed to create GrabbedDeviceHandle: {:?}", e); + } + } + } + + array.into_raw() +} + +/// Create a Java EvdevDeviceInfo object +fn create_java_evdev_device_info( + env: &mut JNIEnv, + name: &str, + bus: i32, + vendor: i32, + product: i32, +) -> Result { + let class = env.find_class("io/github/sds100/keymapper/common/models/EvdevDeviceInfo")?; + let name_str = env.new_string(name)?; + + let obj = env.new_object( + class, + "(Ljava/lang/String;III)V", + &[ + JValue::Object(&name_str.into()), + JValue::Int(bus), + JValue::Int(vendor), + JValue::Int(product), + ], + )?; + + Ok(obj.into_raw()) +} + +/// Create a Java GrabbedDeviceHandle object +fn create_java_grabbed_device_handle( + env: &mut JNIEnv, + id: i32, + name: &str, + bus: i32, + vendor: i32, + product: i32, +) -> Result { + let class = env.find_class("io/github/sds100/keymapper/common/models/GrabbedDeviceHandle")?; + let name_str = env.new_string(name)?; + + let obj = env.new_object( + class, + "(ILjava/lang/String;III)V", + &[ + JValue::Int(id), + JValue::Object(&name_str.into()), + JValue::Int(bus), + JValue::Int(vendor), + JValue::Int(product), + ], + )?; + + Ok(obj.into_raw()) +} diff --git a/evdev/src/main/rust/evdev_manager/jni/src/lib.rs b/evdev/src/main/rust/evdev_manager/jni/src/lib.rs new file mode 100644 index 0000000000..4fcf152074 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/lib.rs @@ -0,0 +1,6 @@ +mod evdev_jni_observer; +mod jni_bridge; +mod logging; + +#[macro_use] +extern crate log; diff --git a/evdev/src/main/rust/evdev_manager/jni/src/logging.rs b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs new file mode 100644 index 0000000000..6f71eeb8ad --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/jni/src/logging.rs @@ -0,0 +1,191 @@ +use jni::objects::{GlobalRef, JValue}; +use jni::JavaVM; +use libc::{c_char, c_int}; +use log::LevelFilter; +use std::ffi::CString; +use std::str::FromStr; +use std::sync::{Arc, OnceLock}; + +/// Holds the JVM and SystemBridge reference for logging callbacks +static KEY_MAPPER_LOGGER: OnceLock = OnceLock::new(); + +#[link(name = "log")] +extern "C" { + pub fn __android_log_write(prio: c_int, tag: *const c_char, text: *const c_char) -> c_int; +} + +/// Custom logger that forwards log messages to Kotlin via JNI +pub struct KeyMapperLogger { + jvm: Arc, + system_bridge: GlobalRef, + tag: CString, +} + +impl KeyMapperLogger { + pub fn init(jvm: Arc, system_bridge: GlobalRef, tag: CString) { + let logger = Self { + jvm, + system_bridge, + tag, + }; + + if KEY_MAPPER_LOGGER.set(logger).is_err() { + panic!("Log callback holder already initialized"); + } + + // Set up the custom JNI logger + // Note: log::set_logger can only be called once per process + if log::set_logger(KEY_MAPPER_LOGGER.get().unwrap()).is_err() { + panic!("Failed to set logger"); + } + + // Set default log level: Info for production builds, Debug for debug builds + let log_level = if cfg!(debug_assertions) { + LevelFilter::Debug + } else { + LevelFilter::Info + }; + + log::set_max_level(log_level); + } + + pub fn set_level(level: AndroidLogLevel) { + log::set_max_level(level.into()); + } + + pub fn set_log_panic_hook() { + std::panic::set_hook(Box::new(|panic_info| { + let mut message = String::from("PANIC in Rust code!"); + + if let Some(location) = panic_info.location() { + message.push_str(&format!( + " at {}:{}:{}", + location.file(), + location.line(), + location.column() + )); + } else { + message.push_str(" at unknown location"); + } + + if let Some(payload) = panic_info.payload().downcast_ref::<&str>() { + message.push_str(&format!(" - {}", payload)); + } else if let Some(payload) = panic_info.payload().downcast_ref::() { + message.push_str(&format!(" - {}", payload)); + } else { + message.push_str(" - unknown payload"); + } + + // Also log via the standard logger for logcat + error!("{}", message); + })); + } + + /// Send a log message to Java via JNI + fn send_log_to_java(&self, level: i32, message: &str) { + let mut env = self + .jvm + .attach_current_thread_permanently() + .expect("Failed to attach to JVM thread"); + + if let Ok(msg) = env.new_string(message) { + let _ = env.call_method( + &self.system_bridge, + "onLogMessage", + "(ILjava/lang/String;)V", + &[JValue::Int(level), JValue::Object(&msg.into())], + ); + } + } +} + +// Safety: JavaVM and GlobalRef are thread-safe +unsafe impl Send for KeyMapperLogger {} +unsafe impl Sync for KeyMapperLogger {} + +impl log::Log for KeyMapperLogger { + fn enabled(&self, metadata: &log::Metadata) -> bool { + metadata.level() <= log::max_level() + } + + fn log(&self, record: &log::Record) { + if !self.enabled(record.metadata()) { + return; + } + + let msg_level: AndroidLogLevel = match record.level() { + log::Level::Error => AndroidLogLevel::Error, + log::Level::Warn => AndroidLogLevel::Warn, + log::Level::Info => AndroidLogLevel::Info, + log::Level::Debug => AndroidLogLevel::Debug, + log::Level::Trace => AndroidLogLevel::Verbose, + }; + + let message = format!("{}", record.args()); + let c_message = CString::from_str(&message).unwrap(); + + // This is taken from the android_log crate. https://crates.io/crates/android_log + unsafe { + __android_log_write(msg_level as c_int, self.tag.as_ptr(), c_message.as_ptr()); + } + + self.send_log_to_java(msg_level as i32, &message); + } + + fn flush(&self) {} +} + +#[derive(Clone, Copy, PartialEq)] +pub enum AndroidLogLevel { + Unknown = 0, + Default, + Verbose, + Debug, + Info, + Warn, + Error, + Fatal, + Silent, +} + +impl From for log::LevelFilter { + fn from(level: AndroidLogLevel) -> Self { + match level { + AndroidLogLevel::Verbose => log::LevelFilter::Trace, + AndroidLogLevel::Debug => log::LevelFilter::Debug, + AndroidLogLevel::Info => log::LevelFilter::Info, + AndroidLogLevel::Warn => log::LevelFilter::Warn, + AndroidLogLevel::Error => log::LevelFilter::Error, + _ => log::LevelFilter::Off, + } + } +} + +impl From for AndroidLogLevel { + fn from(level: log::Level) -> Self { + match level { + log::Level::Trace => AndroidLogLevel::Verbose, + log::Level::Debug => AndroidLogLevel::Debug, + log::Level::Info => AndroidLogLevel::Info, + log::Level::Warn => AndroidLogLevel::Warn, + log::Level::Error => AndroidLogLevel::Error, + } + } +} + +impl From for AndroidLogLevel { + fn from(value: i32) -> Self { + match value { + 0 => AndroidLogLevel::Unknown, + 1 => AndroidLogLevel::Default, + 2 => AndroidLogLevel::Verbose, + 3 => AndroidLogLevel::Debug, + 4 => AndroidLogLevel::Info, + 5 => AndroidLogLevel::Warn, + 6 => AndroidLogLevel::Error, + 7 => AndroidLogLevel::Fatal, + 8 => AndroidLogLevel::Silent, + _ => AndroidLogLevel::Debug, + } + } +} diff --git a/evdev/src/main/rust/evdev_manager/main/Cargo.toml b/evdev/src/main/rust/evdev_manager/main/Cargo.toml new file mode 100644 index 0000000000..aa381c1475 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/main/Cargo.toml @@ -0,0 +1,11 @@ +[package] +name = "main" +version = "0.1.0" +edition = "2021" + +[lib] +name = "evdev_manager" +crate-type = ["cdylib"] + +[dependencies] +evdev_manager_jni = { path = "../jni" } \ No newline at end of file diff --git a/evdev/src/main/rust/evdev_manager/main/src/lib.rs b/evdev/src/main/rust/evdev_manager/main/src/lib.rs new file mode 100644 index 0000000000..d32de18a41 --- /dev/null +++ b/evdev/src/main/rust/evdev_manager/main/src/lib.rs @@ -0,0 +1,2 @@ +#[allow(unused_imports)] +pub use evdev_manager_jni::*; diff --git a/fastlane/Fastfile b/fastlane/Fastfile index 7137a88244..46249c87f1 100644 --- a/fastlane/Fastfile +++ b/fastlane/Fastfile @@ -32,6 +32,12 @@ lane :production do # Don't create changelog for f-droid because not committing it # File.write("metadata/android/en-US/changelogs/" + version_code + ".txt", whats_new) + # Check Rust formatting and run tests + Dir.chdir("../evdev/src/main/rust/evdev_manager") do + sh("cargo fmt --check") + sh("cargo test --package evdev_manager_core") + end + Dir.chdir("..") do gradle(task: "testDebugUnitTest") end @@ -88,6 +94,18 @@ lane :internal do version_code = get_properties_value(key: "VERSION_CODE", path: "./app/version.properties") version_name = get_properties_value(key: "VERSION_NAME", path: "./app/version.properties") + # Check Rust formatting and run tests + Dir.chdir("../evdev/src/main/rust/evdev_manager") do + sh("cargo fmt --check") + sh("cargo test --package evdev_manager_core") + end + + Dir.chdir("..") do + gradle(task: "ktlintFormat") + gradle(task: "ktlintCheck") + gradle(task: "testDebugUnitTest") + end + # Don't create changelog for f-droid because not committing it # File.write("metadata/android/en-US/changelogs/" + version_code + ".txt", whats_new) @@ -101,15 +119,7 @@ lane :internal do secure_text: true ) -# Do not release a debug build for pro version. -# gradle(task: "assembleDebug") - - Dir.chdir("..") do - # Release the free build to GitHub because billing only works if signed by Google Play - gradle(task: "ktlintFormat") - gradle(task: "ktlintCheck") - gradle(task: "testDebugUnitTest") gradle(task: "app:bundleRelease") end diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 9b4fd25842..81be2676ca 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -1,7 +1,7 @@ [versions] compile-sdk = "36" min-sdk = "26" -build-tools = "36.0.0" +build-tools = "36.1.0" target-sdk = "36" android-gradle-plugin = "8.9.1" @@ -39,12 +39,10 @@ epoxy = "4.6.2" flexbox = "3.0.0" google-accompanist-drawablepainter = "0.35.0-alpha" hiddenapibypass = "4.3" -hiddenapibypass-lsposed = "6.1" introshowcaseview = "2.0.2" conscrypt-android = "2.5.3" boringssl-ndk = "20250114" bouncycastle-bcpkix = "1.70" -appiconloader = "1.5.0" rikkax-core = "1.4.1" junit = "4.13.2" junit-params = "1.1.1" @@ -79,6 +77,8 @@ ui-tooling = "1.8.1" # android.arch.persistence.room:testing libsu-core = "6.0.0" rikka-hidden = "4.4.0" +rust-android-gradle = "0.9.6" + [libraries] # Kotlin androidx-hilt-navigation-compose = { module = "androidx.hilt:hilt-navigation-compose", version.ref = "hilt-navigation-compose" } @@ -181,13 +181,11 @@ github-mflisar-dragselectrecyclerview = { group = "com.github.MFlisar", name = " jakewharton-timber = { group = "com.jakewharton.timber", name = "timber", version.ref = "timber" } kotson = { group = "com.github.salomonbrys.kotson", name = "kotson", version.ref = "kotson" } lsposed-hiddenapibypass = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version.ref = "hiddenapibypass" } -lsposed-hiddenapibypass-updated = { group = "org.lsposed.hiddenapibypass", name = "hiddenapibypass", version.ref = "hiddenapibypass-lsposed" } net-lingala-zip4j = { group = "net.lingala.zip4j", name = "zip4j", version.ref = "lingala-zip4j" } github-topjohnwu-libsu = { group = "com.github.topjohnwu.libsu", name = "core", version.ref = "libsu-core" } conscrypt-android = { group = "org.conscrypt", name = "conscrypt-android", version.ref = "conscrypt-android" } vvb2060-ndk-boringssl = { group = "io.github.vvb2060.ndk", name = "boringssl", version.ref = "boringssl-ndk" } bouncycastle-bcpkix = { group = "org.bouncycastle", name = "bcpkix-jdk15on", version.ref = "bouncycastle-bcpkix" } -zhanghai-appiconloader = { group = "me.zhanghai.android.appiconloader", name = "appiconloader", version.ref = "appiconloader" } rikka-rikkax-core = { group = "dev.rikka.rikkax.core", name = "core-ktx", version.ref = "rikkax-core" } @@ -218,6 +216,7 @@ kotlin-parcelize = { id = "org.jetbrains.kotlin.plugin.parcelize", version.ref = kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", version.ref = "kotlin" } android-library = { id = "com.android.library", version.ref = "android-gradle-plugin" } dagger-hilt-android = { id = "com.google.dagger.hilt.android", version.ref = "dagger-hilt-android" } +mozilla-rust-android = { id = "org.mozilla.rust-android-gradle.rust-android", version.ref = "rust-android-gradle" } [bundles] splitties = [ diff --git a/settings.gradle.kts b/settings.gradle.kts index f035ff415c..81636c377b 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -31,3 +31,4 @@ include(":system") include(":common") include(":data") include(":sysbridge") +include(":evdev") diff --git a/sysbridge/NDK_VERSION b/sysbridge/NDK_VERSION deleted file mode 100644 index b48e72d6d9..0000000000 --- a/sysbridge/NDK_VERSION +++ /dev/null @@ -1 +0,0 @@ -27.2.12479018 \ No newline at end of file diff --git a/sysbridge/build.gradle.kts b/sysbridge/build.gradle.kts index c1bf2d10a2..a08999fc43 100644 --- a/sysbridge/build.gradle.kts +++ b/sysbridge/build.gradle.kts @@ -1,5 +1,3 @@ -import kotlin.io.path.absolutePathString - plugins { alias(libs.plugins.android.library) alias(libs.plugins.kotlin.android) @@ -13,18 +11,12 @@ android { namespace = "io.github.sds100.keymapper.sysbridge" compileSdk = libs.versions.compile.sdk.get().toInt() - // Read NDK version from NDK_VERSION file, with fallback to gradle.properties - // The NDK version is stored in a file so the same value can be used across multiple modules. - val ndkVersionFile = project.file("NDK_VERSION") - val ndkVersionFromFile = if (ndkVersionFile.exists()) { - ndkVersionFile.readText().trim() - } else { - null - } - ndkVersion = ndkVersionFromFile!! + ndkVersion = "27.2.12479018" defaultConfig { - // Must be API 29 so that the binder-ndk library can be found. + // System bridge originally only supported Android 10+ because that was the min sdk + // for binder-ndk library. Even though that is no longer used, it is extra effort to support + // the changes to the internal Android APIs on Android 8 and 9. minSdk = 29 testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" @@ -93,6 +85,7 @@ android { dependencies { compileOnly(project(":systemstubs")) implementation(project(":common")) + implementation(project(":data")) implementation(libs.jakewharton.timber) @@ -105,115 +98,11 @@ dependencies { implementation(libs.rikka.shizuku.api) implementation(libs.rikka.shizuku.provider) implementation(libs.rikka.hidden.compat) + implementation(libs.androidx.datastore.preferences) compileOnly(libs.rikka.hidden.stub) // From Shizuku :manager module build.gradle file. implementation(libs.vvb2060.ndk.boringssl) - implementation(libs.lsposed.hiddenapibypass.updated) implementation(libs.bouncycastle.bcpkix) - implementation(libs.zhanghai.appiconloader) implementation(libs.rikka.rikkax.core) } - -tasks.named("preBuild") { - dependsOn(generateLibEvDevEventNames) - dependsOn(compileAidlNdk) -} - -// The list of event names needs to be parsed from the input.h file in the NDK. -// input.h can be found in the Android/sdk/ndk/27.0.12077973/toolchains/llvm/prebuilt/darwin-x86_64/sysroot/usr/include/linux/input.h -// folder on macOS. -val generateLibEvDevEventNames by tasks.registering(Exec::class) { - dependsOn(compileAidlNdk) - - group = "build" - description = "Generates event names header from input.h" - - val prebuiltDir = File(android.ndkDirectory, "toolchains/llvm/prebuilt") - - // The "darwin-x86_64" part of the path is different on each operating system but it seems like - // the SDK Manager only downloads the NDK specific to the local operating system. So, just - // go into the only directory that the "prebuilt" directory contains. - val hostDirs = prebuiltDir.listFiles { file -> file.isDirectory } - ?: throw GradleException("No prebuilt toolchain directories found in $prebuiltDir") - - if (hostDirs.size != 1) { - throw GradleException( - "Expected exactly one prebuilt toolchain directory in $prebuiltDir, found ${hostDirs.size}", - ) - } - val toolchainDir = hostDirs[0].absolutePath - - val inputHeader = "$toolchainDir/sysroot/usr/include/linux/input.h" - val inputEventCodesHeader = "$toolchainDir/sysroot/usr/include/linux/input-event-codes.h" - val outputHeader = "$projectDir/src/main/cpp/libevdev/event-names.h" - val pythonScript = "$projectDir/src/main/cpp/libevdev/make-event-names.py" - - commandLine("python3", pythonScript, inputHeader, inputEventCodesHeader) - - standardOutput = File(outputHeader).outputStream() - - inputs.file(pythonScript) - inputs.file(inputHeader) - inputs.file(inputEventCodesHeader) - outputs.file(outputHeader) -} - -// Task to compile AIDL files for NDK. -// Taken from https://github.com/lakinduboteju/AndroidNdkBinderExamples -val compileAidlNdk by tasks.registering(Exec::class) { - group = "build" - description = "Compiles AIDL files in src/main/aidl to NDK C++ headers and sources." - - val aidlSrcDir = project.file("src/main/aidl") - // Find all .aidl files. Using fileTree ensures it's dynamic. - val aidlFiles = project.fileTree(aidlSrcDir) { - include("**/IEvdevCallback.aidl") - include("**/InputDeviceIdentifier.aidl") - } - - inputs.files(aidlFiles) - .withPathSensitivity(PathSensitivity.RELATIVE) - .withPropertyName("aidlInputFiles") - - val cppOutDir = project.file("src/main/cpp/aidl") - val cppHeaderOutDir = project.file("src/main/cpp") - - outputs.dir(cppOutDir).withPropertyName("cppOutputDir") - outputs.dir(cppHeaderOutDir).withPropertyName("cppHeaderOutputDir") - - // Path to the aidl executable in the Android SDK - val aidlToolPath = - android.sdkDirectory.toPath() - .resolve("build-tools") - .resolve(android.buildToolsVersion) - .resolve("aidl") - .absolutePathString() - val importSearchPath = aidlSrcDir.absolutePath - - // Ensure output directories exist before trying to write to them - cppOutDir.mkdirs() - cppHeaderOutDir.mkdirs() - - if (aidlFiles.isEmpty) { - logger.info("No AIDL files found in $aidlSrcDir. Skipping compileAidlNdk task.") - return@registering // Exit doLast if no files to process - } - - for (aidlFile in aidlFiles) { - logger.lifecycle("Compiling AIDL file (NDK): ${aidlFile.path}") - - commandLine( - aidlToolPath, - "--lang=ndk", - "-o", cppOutDir.absolutePath, - "-h", cppHeaderOutDir.absolutePath, - "-I", importSearchPath, - aidlFile.absolutePath, - ) - } - - logger.lifecycle( - "AIDL NDK compilation finished. Check outputs in $cppOutDir and $cppHeaderOutDir", - ) -} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/ADD_TO_PROGUARD.txt b/sysbridge/src/main/aidl/io/github/sds100/keymapper/ADD_TO_PROGUARD.txt new file mode 100644 index 0000000000..c89cbd2713 --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/ADD_TO_PROGUARD.txt @@ -0,0 +1 @@ +do not forget to add exemptions for AIDL files to proguard \ No newline at end of file diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl new file mode 100644 index 0000000000..1ffea8fb99 --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/evdev/IEvdevCallback.aidl @@ -0,0 +1,15 @@ +package io.github.sds100.keymapper.evdev; + +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; + +interface IEvdevCallback { + /** + * deviceId is the internal system bridge ID for the device. This is used rather than referencing + * with a path because primitives have lower overhead and are safer over the JNI boundary. + */ + boolean onEvdevEvent(int deviceId, long timeSec, long timeUsec, int type, int code, int value, int androidCode); + void onEmergencyKillSystemBridge(); + void onGrabbedDevicesChanged(in GrabbedDeviceHandle[] devices); + void onEvdevDevicesChanged(in EvdevDeviceInfo[] devices); +} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl deleted file mode 100644 index f6b4039a6d..0000000000 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.aidl +++ /dev/null @@ -1,7 +0,0 @@ -package io.github.sds100.keymapper.sysbridge; - -interface IEvdevCallback { - oneway void onEvdevEventLoopStarted(); - boolean onEvdevEvent(String devicePath, long timeSec, long timeUsec, int type, int code, int value, int androidCode); - void onEmergencyKillSystemBridge(); -} \ No newline at end of file diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl new file mode 100644 index 0000000000..eb1adb4835 --- /dev/null +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ILogCallback.aidl @@ -0,0 +1,10 @@ +package io.github.sds100.keymapper.sysbridge; + +interface ILogCallback { + /** + * Called when a log message is emitted from the SystemBridge. + * @param level The log level: 0=ERROR, 1=WARN, 2=INFO, 3=DEBUG + * @param message The log message (already prefixed with "systembridge:") + */ + void onLog(int level, String message); +} diff --git a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl index 64f7a0afe3..ee04b124c8 100644 --- a/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl +++ b/sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl @@ -1,7 +1,10 @@ package io.github.sds100.keymapper.sysbridge; -import io.github.sds100.keymapper.sysbridge.IEvdevCallback; -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle; +import io.github.sds100.keymapper.evdev.IEvdevCallback; +import io.github.sds100.keymapper.sysbridge.ILogCallback; +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo; +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode; +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle; import io.github.sds100.keymapper.common.models.ShellResult; import android.view.InputEvent; @@ -11,19 +14,15 @@ interface ISystemBridge { int getVersionCode() = 16777112; ShellResult executeCommand(String command, long timeoutMillis) = 16777111; - boolean grabEvdevDevice(String devicePath) = 1; - boolean grabEvdevDeviceArray(in String[] devicePath) = 2; - - boolean ungrabEvdevDevice(String devicePath) = 3; - boolean ungrabAllEvdevDevices() = 4; + GrabbedDeviceHandle[] setGrabTargets(in GrabTargetKeyCode[] devices) = 1; void registerEvdevCallback(IEvdevCallback callback) = 5; void unregisterEvdevCallback() = 6; - boolean writeEvdevEvent(String devicePath, int type, int code, int value) = 7; + boolean writeEvdevEvent(int deviceId, int type, int code, int value) = 7; boolean injectInputEvent(in InputEvent event, int mode) = 8; - EvdevDeviceHandle[] getEvdevInputDevices() = 9; + EvdevDeviceInfo[] getEvdevInputDevices() = 9; boolean setWifiEnabled(boolean enable) = 10; @@ -48,4 +47,10 @@ interface ISystemBridge { void setTetheringEnabled(boolean enable) = 20; long getUsbScreenUnlockedFunctions() = 21; -} \ No newline at end of file + + boolean writeEvdevEventKeyCode(int deviceId, int keyCode, int value) = 22; + + void registerLogCallback(ILogCallback callback) = 23; + void unregisterLogCallback() = 24; + void setLogLevel(int level) = 25; +} diff --git a/sysbridge/src/main/cpp/CMakeLists.txt b/sysbridge/src/main/cpp/CMakeLists.txt index 5781ee5879..b7eef6b73f 100644 --- a/sysbridge/src/main/cpp/CMakeLists.txt +++ b/sysbridge/src/main/cpp/CMakeLists.txt @@ -59,68 +59,4 @@ target_link_libraries(adb ${log-lib} boringssl::crypto_static) if (NOT CMAKE_BUILD_TYPE STREQUAL "Debug") add_custom_command(TARGET adb POST_BUILD COMMAND ${CMAKE_STRIP} --remove-section=.comment "${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/libadb.so") -endif () - -# Creates and names a library, sets it as either STATIC -# or SHARED, and provides the relative paths to its source code. -# You can define multiple libraries, and CMake builds them for you. -# Gradle automatically packages shared libraries with your APK. -# -# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define -# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME} -# is preferred for the same purpose. -# -# In order to load a library into your app from Java/Kotlin, you must call -# System.loadLibrary() and pass the name of the library defined here; -# for GameActivity/NativeActivity derived applications, the same library name must be -# used in the AndroidManifest.xml file. -add_library(evdev SHARED - # List C/C++ source files with relative paths to this CMakeLists.txt. - libevdev_jni.cpp - libevdev/libevdev.c - libevdev/libevdev-names.c - libevdev/libevdev-uinput.c - android/input/KeyLayoutMap.cpp - android/input/InputEventLabels.cpp - android/libbase/result.cpp - android/utils/Tokenizer.cpp - android/utils/String16.cpp - android/utils/String8.cpp - android/utils/SharedBuffer.cpp - android/utils/FileMap.cpp - android/utils/Unicode.cpp - android/input/InputDevice.cpp - android/input/Input.cpp - android/libbase/stringprintf.cpp - ${aidl_src_dir}/io/github/sds100/keymapper/sysbridge/IEvdevCallback.cpp) - -# Set a stable custom output directory for libevdev.so so it can be linked to in other modules -# Outputs to custom "libs" directory in the sysbridge module build directory -set_target_properties(evdev PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}" - LIBRARY_OUTPUT_DIRECTORY_DEBUG "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}" - LIBRARY_OUTPUT_DIRECTORY_RELEASE "${CMAKE_CURRENT_SOURCE_DIR}/../../../build/libs/${ANDROID_ABI}") - -find_library( - binder_ndk-lib - binder_ndk -) - -# Specifies libraries CMake should link to your target library. You -# can link libraries from various origins, such as libraries defined in this -# build script, prebuilt third-party libraries, or Android system libraries. -target_link_libraries(evdev - # List libraries link to the target library - android - log - ${binder_ndk-lib}) - -# Add include directories for header files -target_include_directories(evdev PRIVATE - ${CMAKE_CURRENT_SOURCE_DIR} - ${CMAKE_CURRENT_SOURCE_DIR}/android - ${CMAKE_CURRENT_SOURCE_DIR}/android/input - ${CMAKE_CURRENT_SOURCE_DIR}/android/libbase - ${CMAKE_CURRENT_SOURCE_DIR}/android/utils - ${CMAKE_CURRENT_SOURCE_DIR}/libevdev) - +endif () \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/ftl/algorithm.h b/sysbridge/src/main/cpp/android/ftl/algorithm.h deleted file mode 100644 index 81993de177..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/algorithm.h +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ftl { - -// Determines if a container contains a value. This is a simplified version of the C++23 -// std::ranges::contains function. -// -// const ftl::StaticVector vector = {1, 2, 3}; -// assert(ftl::contains(vector, 1)); -// -// TODO: Remove in C++23. - template - auto contains(const Container &container, const Value &value) -> bool { - return std::find(container.begin(), container.end(), value) != container.end(); - } - -// Adapter for std::find_if that converts the return value from iterator to optional. -// -// const ftl::StaticVector vector = {"upside"sv, "down"sv, "cake"sv}; -// assert(ftl::find_if(vector, [](const auto& str) { return str.front() == 'c'; }) == "cake"sv); -// - template - constexpr auto find_if(const Container &container, Predicate &&predicate) - -> Optional> { - const auto it = std::find_if(std::cbegin(container), std::cend(container), - std::forward(predicate)); - if (it == std::cend(container)) return {}; - return std::cref(*it); - } - -// Transformers for ftl::find_if on a map-like `Container` that contains key-value pairs. -// -// const ftl::SmallMap map = ftl::init::map>( -// 12, "snow"sv, "cone"sv)(13, "tiramisu"sv)(14, "upside"sv, "down"sv, "cake"sv); -// -// using Map = decltype(map); -// -// assert(14 == ftl::find_if(map, [](const auto& pair) { -// return pair.second.size() == 3; -// }).transform(ftl::to_key)); -// -// const auto opt = ftl::find_if(map, [](const auto& pair) { -// return pair.second.size() == 1; -// }).transform(ftl::to_mapped_ref); -// -// assert(opt); -// assert(opt->get() == ftl::StaticVector("tiramisu"sv)); -// - template - constexpr auto to_key(const Pair &pair) -> Key { - return pair.first; - } - - template - constexpr auto to_mapped_ref(const Pair &pair) -> std::reference_wrapper { - return std::cref(pair.second); - } - -// Combinator for ftl::Optional::or_else when T is std::reference_wrapper. Given a -// lambda argument that returns a `constexpr` value, ftl::static_ref binds a reference to a -// static T initialized to that constant. -// -// const ftl::SmallMap map = ftl::init::map(13, "tiramisu"sv)(14, "upside-down cake"sv); -// assert("???"sv == -// map.get(20).or_else(ftl::static_ref([] { return "???"sv; }))->get()); -// -// using Map = decltype(map); -// -// assert("snow cone"sv == -// ftl::find_if(map, [](const auto& pair) { return pair.second.front() == 's'; }) -// .transform(ftl::to_mapped_ref) -// .or_else(ftl::static_ref([] { return "snow cone"sv; })) -// ->get()); -// - template - constexpr auto static_ref(F &&f) { - return [f = std::forward(f)] { - constexpr auto kInitializer = f(); - static const T kValue = kInitializer; - return Optional(std::cref(kValue)); - }; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/cast.h b/sysbridge/src/main/cpp/android/ftl/cast.h deleted file mode 100644 index 64129a1a58..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/cast.h +++ /dev/null @@ -1,86 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - - enum class CastSafety { - kSafe, kUnderflow, kOverflow - }; - -// Returns whether static_cast(v) is safe, or would result in underflow or overflow. -// -// static_assert(ftl::cast_safety(-1) == ftl::CastSafety::kUnderflow); -// static_assert(ftl::cast_safety(128u) == ftl::CastSafety::kOverflow); -// -// static_assert(ftl::cast_safety(-.1f) == ftl::CastSafety::kUnderflow); -// static_assert(ftl::cast_safety(static_cast(INT32_MAX)) == -// ftl::CastSafety::kOverflow); -// -// static_assert(ftl::cast_safety(-DBL_MAX) == ftl::CastSafety::kUnderflow); -// - template - constexpr CastSafety cast_safety(T v) { - static_assert(std::is_arithmetic_v); - static_assert(std::is_arithmetic_v); - - constexpr bool kFromSigned = std::is_signed_v; - constexpr bool kToSigned = std::is_signed_v; - - using details::max_exponent; - - // If the R range contains the T range, then casting is always safe. - if constexpr ((kFromSigned == kToSigned && max_exponent >= max_exponent) || - (!kFromSigned && kToSigned && max_exponent > max_exponent)) { - return CastSafety::kSafe; - } - - using C = std::common_type_t; - - if constexpr (kFromSigned) { - using L = details::safe_limits; - - if constexpr (kToSigned) { - // Signed to signed. - if (v < L::lowest()) return CastSafety::kUnderflow; - return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; - } else { - // Signed to unsigned. - if (v < 0) return CastSafety::kUnderflow; - return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe - : CastSafety::kOverflow; - } - } else { - using L = std::numeric_limits; - - if constexpr (kToSigned) { - // Unsigned to signed. - return static_cast(v) <= static_cast(L::max()) ? CastSafety::kSafe - : CastSafety::kOverflow; - } else { - // Unsigned to unsigned. - return v <= L::max() ? CastSafety::kSafe : CastSafety::kOverflow; - } - } - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/concat.h b/sysbridge/src/main/cpp/android/ftl/concat.h deleted file mode 100644 index b826a2e858..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/concat.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// Lightweight (not allocating nor sprintf-based) concatenation. The variadic arguments can be -// values of integral type (including bool and char), string literals, or strings whose length -// is constrained: -// -// std::string_view name = "Volume"; -// ftl::Concat string(ftl::truncated<3>(name), ": ", -3, " dB"); -// -// assert(string.str() == "Vol: -3 dB"); -// assert(string.c_str()[string.size()] == '\0'); -// - template - struct Concat; - - template - struct Concat : Concat::N, Ts...> { - explicit constexpr Concat(T v, Ts... args) { append(v, args...); } - - protected: - constexpr Concat() = default; - - constexpr void append(T v, Ts... args) { - using Str = details::StaticString; - const Str str(v); - - // TODO: Replace with constexpr std::copy in C++20. - for (auto it = str.view.begin(); it != str.view.end();) { - *this->end_++ = *it++; - } - - using Base = Concat; - this->Base::append(args...); - } - }; - - template - struct Concat { - static constexpr std::size_t max_size() { return N; } - - constexpr std::size_t size() const { return static_cast(end_ - buffer_); } - - constexpr const char *c_str() const { return buffer_; } - - constexpr std::string_view str() const { - // TODO: Replace with {buffer_, end_} in C++20. - return {buffer_, size()}; - } - - protected: - constexpr Concat() : end_(buffer_) {} - - constexpr Concat(const Concat &) = delete; - - constexpr void append() { *end_ = '\0'; } - - char buffer_[N + 1]; - char *end_; - }; - -// Deduction guide. - template - Concat(Ts &&...) -> Concat<0, Ts...>; - - template - constexpr auto truncated(std::string_view v) { - return details::Truncated{v}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/array_traits.h b/sysbridge/src/main/cpp/android/ftl/details/array_traits.h deleted file mode 100644 index f66742598c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/array_traits.h +++ /dev/null @@ -1,181 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#define FTL_ARRAY_TRAIT(T, U) using U = typename details::ArrayTraits::U - -namespace android::ftl::details { - - template - struct ArrayTraits { - using value_type = T; - using size_type = std::size_t; - using difference_type = std::ptrdiff_t; - - using pointer = value_type *; - using reference = value_type &; - using iterator = pointer; - using reverse_iterator = std::reverse_iterator; - - using const_pointer = const value_type *; - using const_reference = const value_type &; - using const_iterator = const_pointer; - using const_reverse_iterator = std::reverse_iterator; - - template - static constexpr pointer construct_at(const_iterator it, Args &&... args) { - void *const ptr = const_cast(static_cast(it)); - if constexpr (std::is_constructible_v) { - // TODO: Replace with std::construct_at in C++20. - return new(ptr) value_type(std::forward(args)...); - } else { - // Fall back to list initialization. - return new(ptr) value_type{std::forward(args)...}; - } - } - - // TODO: Make constexpr in C++20. - template - static reference replace_at(const_iterator it, Args &&... args) { - value_type element{std::forward(args)...}; - return replace_at(it, std::move(element)); - } - - // TODO: Make constexpr in C++20. - static reference replace_at(const_iterator it, value_type &&value) { - std::destroy_at(it); - // This is only safe because exceptions are disabled. - return *construct_at(it, std::move(value)); - } - - // TODO: Make constexpr in C++20. - static void in_place_swap(reference a, reference b) { - value_type c{std::move(a)}; - replace_at(&a, std::move(b)); - replace_at(&b, std::move(c)); - } - - // TODO: Make constexpr in C++20. - static void in_place_swap_ranges(iterator first1, iterator last1, iterator first2) { - while (first1 != last1) { - in_place_swap(*first1++, *first2++); - } - } - - // TODO: Replace with std::uninitialized_copy in C++20. - template - static void uninitialized_copy(Iterator first, Iterator last, const_iterator out) { - while (first != last) { - construct_at(out++, *first++); - } - } - }; - -// CRTP mixin to define iterator functions in terms of non-const Self::begin and Self::end. - template - class ArrayIterators { - FTL_ARRAY_TRAIT(T, size_type); - - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - Self &self() const { return *const_cast(static_cast(this)); } - - public: - const_iterator begin() const { return cbegin(); } - - const_iterator cbegin() const { return self().begin(); } - - const_iterator end() const { return cend(); } - - const_iterator cend() const { return self().end(); } - - reverse_iterator rbegin() { return std::make_reverse_iterator(self().end()); } - - const_reverse_iterator rbegin() const { return crbegin(); } - - const_reverse_iterator crbegin() const { return self().rbegin(); } - - reverse_iterator rend() { return std::make_reverse_iterator(self().begin()); } - - const_reverse_iterator rend() const { return crend(); } - - const_reverse_iterator crend() const { return self().rend(); } - - iterator last() { return self().end() - 1; } - - const_iterator last() const { return self().last(); } - - reference front() { return *self().begin(); } - - const_reference front() const { return self().front(); } - - reference back() { return *last(); } - - const_reference back() const { return self().back(); } - - reference operator[](size_type i) { return *(self().begin() + i); } - - const_reference operator[](size_type i) const { return self()[i]; } - }; - -// Mixin to define comparison operators for an array-like template. -// TODO: Replace with operator<=> in C++20. - template class Array> - struct ArrayComparators { - template - friend bool operator==(const Array &lhs, const Array &rhs) { - return lhs.size() == rhs.size() && std::equal(lhs.begin(), lhs.end(), rhs.begin()); - } - - template - friend bool operator<(const Array &lhs, const Array &rhs) { - return std::lexicographical_compare(lhs.begin(), lhs.end(), rhs.begin(), rhs.end()); - } - - template - friend bool operator>(const Array &lhs, const Array &rhs) { - return rhs < lhs; - } - - template - friend bool operator!=(const Array &lhs, const Array &rhs) { - return !(lhs == rhs); - } - - template - friend bool operator>=(const Array &lhs, const Array &rhs) { - return !(lhs < rhs); - } - - template - friend bool operator<=(const Array &lhs, const Array &rhs) { - return !(lhs > rhs); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/cast.h b/sysbridge/src/main/cpp/android/ftl/details/cast.h deleted file mode 100644 index 8196ba61a7..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/cast.h +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - -// Exponent whose power of 2 is the (exclusive) upper bound of T. - template> - constexpr int max_exponent = std::is_floating_point_v ? L::max_exponent : L::digits; - -// Extension of std::numeric_limits that reduces the maximum for integral types T such that it -// has an exact representation for floating-point types F. For example, the maximum int32_t value -// is 2'147'483'647, but casting it to float commonly rounds up to 2'147'483'650.f, which cannot -// be safely converted back lest the signed overflow invokes undefined behavior. This pitfall is -// avoided by clearing the lower (31 - 24 =) 7 bits of precision to 2'147'483'520. Note that the -// minimum is representable. - template - struct safe_limits : std::numeric_limits { - static constexpr T max() { - using Base = std::numeric_limits; - - if constexpr (std::is_integral_v && std::is_floating_point_v) { - // Assume the mantissa is 24 bits for float, or 53 bits for double. - using Float = std::numeric_limits; - static_assert(Float::is_iec559); - - // If the integer is wider than the mantissa, clear the excess bits of precision. - constexpr int kShift = Base::digits - Float::digits; - if constexpr (kShift > 0) { - using U = std::make_unsigned_t; - constexpr U kOne = static_cast(1); - return static_cast(Base::max()) & ~((kOne << kShift) - kOne); - } - } - - return Base::max(); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/concat.h b/sysbridge/src/main/cpp/android/ftl/details/concat.h deleted file mode 100644 index c1462a658c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/concat.h +++ /dev/null @@ -1,91 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include - -namespace android::ftl::details { - - template - struct StaticString; - -// Booleans. - template - struct StaticString>> { - static constexpr std::size_t N = 5; // Length of "false". - - explicit constexpr StaticString(bool b) : view(b ? "true" : "false") {} - - const std::string_view view; - }; - -// Characters. - template - struct StaticString>> { - static constexpr std::size_t N = 1; - - explicit constexpr StaticString(char c) : character(c) {} - - const char character; - const std::string_view view{&character, 1u}; - }; - -// Integers, including the integer value of other character types like char32_t. - template - struct StaticString< - T, std::enable_if_t< - std::is_integral_v> && !is_bool_v && !is_char_v>> { - using U = remove_cvref_t; - static constexpr std::size_t N = to_chars_length_v; - - // TODO: Mark this and to_chars as `constexpr` in C++23. - explicit StaticString(U v) : view(to_chars(buffer, v)) {} - - to_chars_buffer_t buffer; - const std::string_view view; - }; - -// Character arrays. - template - struct StaticString { - static constexpr std::size_t N = M - 1; - - explicit constexpr StaticString(const char (&str)[M]) : view(str, N) {} - - const std::string_view view; - }; - - template - struct Truncated { - std::string_view view; - }; - -// Strings with constrained length. - template - struct StaticString, void> { - static constexpr std::size_t N = M; - - explicit constexpr StaticString(Truncated str) : view(str.view.substr(0, N)) {} - - const std::string_view view; - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/function.h b/sysbridge/src/main/cpp/android/ftl/details/function.h deleted file mode 100644 index 8d7fc9bd58..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/function.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android::ftl::details { - -// The maximum allowed value for the template argument `N` in -// `ftl::Function`. - constexpr size_t kFunctionMaximumN = 14; - -// Converts a member function pointer type `Ret(Class::*)(Args...)` to an equivalent non-member -// function type `Ret(Args...)`. - - template - struct remove_member_function_pointer; - - template - struct remove_member_function_pointer { - using type = Ret(Args...); - }; - - template - struct remove_member_function_pointer { - using type = Ret(Args...); - }; - - template - using remove_member_function_pointer_t = - typename remove_member_function_pointer::type; - -// Helper functions for binding to the supported targets. - - template - auto bind_opaque_no_op() -> Ret (*)(void *, Args...) { - return [](void *, Args...) -> Ret { - if constexpr (!std::is_void_v) { - return Ret{}; - } - }; - } - - template - auto bind_opaque_function_object(const F &) -> Ret (*)(void *, Args...) { - return [](void *opaque, Args... args) -> Ret { - return std::invoke(*static_cast(opaque), std::forward(args)...); - }; - } - - template - auto bind_member_function(Class *instance, Ret (*)(Args...) = nullptr) { - return [instance](Args... args) -> Ret { - return std::invoke(MemberFunction, instance, std::forward(args)...); - }; - } - - template - auto bind_free_function(Ret (*)(Args...) = nullptr) { - return [](Args... args) -> Ret { - return std::invoke(FreeFunction, std::forward(args)...); - }; - } - -// Traits class for the opaque storage used by Function. - - template - struct function_opaque_storage { - // The actual type used for the opaque storage. An `N` of zero specifies the minimum useful size, - // which allows a lambda with zero or one capture args. - using type = std::array; - - template - static constexpr bool require_trivially_copyable = std::is_trivially_copyable_v; - - template - static constexpr bool require_trivially_destructible = std::is_trivially_destructible_v; - - template - static constexpr bool require_will_fit_in_opaque_storage = sizeof(S) <= sizeof(type); - - template - static constexpr bool require_alignment_compatible = - std::alignment_of_v <= std::alignment_of_v; - - // Copies `src` into the opaque storage, and returns that storage. - template - static type opaque_copy(const S &src) { - // TODO: Replace with C++20 concepts/constraints which can give more details. - static_assert(require_trivially_copyable, - "ftl::Function can only store lambdas that capture trivially copyable data."); - static_assert( - require_trivially_destructible, - "ftl::Function can only store lambdas that capture trivially destructible data."); - static_assert(require_will_fit_in_opaque_storage, - "ftl::Function has limited storage for lambda captured state. Maybe you need to " - "increase N?"); - static_assert(require_alignment_compatible); - - type opaque; - std::memcpy(opaque.data(), &src, sizeof(S)); - return opaque; - } - }; - -// Traits class to help determine the template parameters to use for a ftl::Function, given a -// function object. - - template - struct function_traits { - // The function type `F` with which to instantiate the `Function` template. - using type = remove_member_function_pointer_t<&F::operator()>; - - // The (minimum) size `N` with which to instantiate the `Function` template. - static constexpr std::size_t size = - (std::max(sizeof(std::intptr_t), sizeof(F)) - 1) / sizeof(std::intptr_t); - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/future.h b/sysbridge/src/main/cpp/android/ftl/details/future.h deleted file mode 100644 index ec3926ba8c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/future.h +++ /dev/null @@ -1,119 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl { - - template class> - class Future; - - namespace details { - - template - struct future_result { - using type = T; - }; - - template - struct future_result> { - using type = T; - }; - - template - struct future_result> { - using type = T; -}; - -template class FutureImpl> -struct future_result> { -using type = T; -}; - -template -using future_result_t = typename future_result::type; - -struct ValueTag { -}; - -template class> -class BaseFuture; - -template -class BaseFuture { - using Impl = std::future; - -public: - Future share() { - if (T *value = std::get_if(&self())) { - return {ValueTag{}, std::move(*value)}; - } - - return std::get(self()).share(); - } - -protected: - T get() { - if (T *value = std::get_if(&self())) { - return std::move(*value); - } - - return std::get(self()).get(); - } - - template - std::future_status wait_for(const std::chrono::duration &timeout_duration) const { - if (std::holds_alternative(self())) { - return std::future_status::ready; - } - - return std::get(self()).wait_for(timeout_duration); - } - -private: - auto &self() { return static_cast(*this).future_; } - - const auto &self() const { return static_cast(*this).future_; } -}; - -template -class BaseFuture { - using Impl = std::shared_future; - -protected: - const T &get() const { - if (const T *value = std::get_if(&self())) { - return *value; - } - - return std::get(self()).get(); - } - - template - std::future_status wait_for(const std::chrono::duration &timeout_duration) const { - if (std::holds_alternative(self())) { - return std::future_status::ready; - } - - return std::get(self()).wait_for(timeout_duration); - } - -private: - const auto &self() const { return static_cast(*this).future_; } -}; - -} // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/hash.h b/sysbridge/src/main/cpp/android/ftl/details/hash.h deleted file mode 100644 index cb9939036a..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/hash.h +++ /dev/null @@ -1,125 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - -// Based on CityHash64 v1.0.1 (http://code.google.com/p/cityhash/), but slightly -// modernized and trimmed for cases with bounded lengths. - - template - inline T read_unaligned(const void *ptr) { - T v; - std::memcpy(&v, ptr, sizeof(T)); - return v; - } - - template - constexpr std::uint64_t rotate(std::uint64_t v, std::uint8_t shift) { - if constexpr (!NonZeroShift) { - if (shift == 0) return v; - } - return (v >> shift) | (v << (64 - shift)); - } - - constexpr std::uint64_t shift_mix(std::uint64_t v) { - return v ^ (v >> 47); - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - constexpr std::uint64_t hash_length_16(std::uint64_t u, std::uint64_t v) { - constexpr std::uint64_t kPrime = 0x9ddfea08eb382d69ull; - auto a = (u ^ v) * kPrime; - a ^= (a >> 47); - auto b = (v ^ a) * kPrime; - b ^= (b >> 47); - b *= kPrime; - return b; - } - - constexpr std::uint64_t kPrime0 = 0xc3a5c85c97cb3127ull; - constexpr std::uint64_t kPrime1 = 0xb492b66fbe98f273ull; - constexpr std::uint64_t kPrime2 = 0x9ae16a3b2f90404full; - constexpr std::uint64_t kPrime3 = 0xc949d7c7509e6557ull; - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_0_to_16(const char *str, std::uint64_t length) { - if (length > 8) { - const auto a = read_unaligned(str); - const auto b = read_unaligned(str + length - 8); - return hash_length_16(a, rotate(b + length, static_cast(length))) ^ - b; - } - if (length >= 4) { - const auto a = read_unaligned(str); - const auto b = read_unaligned(str + length - 4); - return hash_length_16(length + (a << 3), b); - } - if (length > 0) { - const auto a = static_cast(str[0]); - const auto b = static_cast(str[length >> 1]); - const auto c = static_cast(str[length - 1]); - const auto y = static_cast(a) + (static_cast(b) << 8); - const auto z = - static_cast(length) + (static_cast(c) << 2); - return shift_mix(y * kPrime2 ^ z * kPrime3) * kPrime2; - } - return kPrime2; - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_17_to_32(const char *str, std::uint64_t length) { - const auto a = read_unaligned(str) * kPrime1; - const auto b = read_unaligned(str + 8); - const auto c = read_unaligned(str + length - 8) * kPrime2; - const auto d = read_unaligned(str + length - 16) * kPrime0; - return hash_length_16(rotate(a - b, 43) + rotate(c, 30) + d, - a + rotate(b ^ kPrime3, 20) - c + length); - } - - __attribute__((no_sanitize("unsigned-integer-overflow"))) - inline std::uint64_t hash_length_33_to_64(const char *str, std::uint64_t length) { - auto z = read_unaligned(str + 24); - auto a = read_unaligned(str) + (length + read_unaligned(str + length - 16)) * kPrime0; - auto b = rotate(a + z, 52); - auto c = rotate(a, 37); - - a += read_unaligned(str + 8); - c += rotate(a, 7); - a += read_unaligned(str + 16); - - const auto vf = a + z; - const auto vs = b + rotate(a, 31) + c; - - a = read_unaligned(str + 16) + read_unaligned(str + length - 32); - z += read_unaligned(str + length - 8); - b = rotate(a + z, 52); - c = rotate(a, 37); - a += read_unaligned(str + length - 24); - c += rotate(a, 7); - a += read_unaligned(str + length - 16); - - const auto wf = a + z; - const auto ws = b + rotate(a, 31) + c; - const auto r = shift_mix((vf + ws) * kPrime2 + (wf + vs) * kPrime0); - return shift_mix(r * kPrime0 + vs) * kPrime2; - } - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/match.h b/sysbridge/src/main/cpp/android/ftl/details/match.h deleted file mode 100644 index cee77c83ee..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/match.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl::details { - - template - struct Matcher : Ms ... { - using Ms::operator()...; - }; - -// Deduction guide. - template - Matcher(Ms...) -> Matcher; - - template - constexpr bool is_exhaustive_match_v = (std::is_invocable_v && ...); - - template - struct Match; - - template - struct Match { - template - static decltype(auto) match(Variant &variant, const Matcher &matcher) { - if (auto *const ptr = std::get_if(&variant)) { - return matcher(*ptr); - } else { - return Match::match(variant, matcher); - } - } - }; - - template - struct Match { - template - static decltype(auto) match(Variant &variant, const Matcher &matcher) { - return matcher(std::get(variant)); - } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/mixins.h b/sysbridge/src/main/cpp/android/ftl/details/mixins.h deleted file mode 100644 index a52d74c504..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/mixins.h +++ /dev/null @@ -1,31 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl::details { - - template class> - class Mixin { - protected: - constexpr Self &self() { return *static_cast(this); } - - constexpr const Self &self() const { return *static_cast(this); } - - constexpr auto &mut() { return self().value_; } - }; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/details/optional.h b/sysbridge/src/main/cpp/android/ftl/details/optional.h deleted file mode 100644 index bcbd8826c0..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/optional.h +++ /dev/null @@ -1,72 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - - template - struct Optional; - - namespace details { - - template - struct is_optional : std::false_type { - }; - - template - struct is_optional> : std::true_type { - }; - - template - struct is_optional> : std::true_type { - }; - - template - struct transform_result { - using type = Optional>>; - }; - - template - using transform_result_t = typename transform_result::type; - - template - struct and_then_result { - using type = remove_cvref_t>; - static_assert(is_optional{}, "and_then function must return an optional"); - }; - - template - using and_then_result_t = typename and_then_result::type; - - template - struct or_else_result { - using type = remove_cvref_t>; - static_assert( - std::is_same_v> || std::is_same_v>, - "or_else function must return an optional T"); - }; - - template - using or_else_result_t = typename or_else_result::type; - - } // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/details/type_traits.h b/sysbridge/src/main/cpp/android/ftl/details/type_traits.h deleted file mode 100644 index 70a602c330..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/details/type_traits.h +++ /dev/null @@ -1,33 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl::details { - -// TODO: Replace with std::remove_cvref_t in C++20. - template - using remove_cvref_t = std::remove_cv_t>; - - template - constexpr bool is_bool_v = std::is_same_v, bool>; - - template - constexpr bool is_char_v = std::is_same_v, char>; - -} // namespace android::ftl::details diff --git a/sysbridge/src/main/cpp/android/ftl/enum.h b/sysbridge/src/main/cpp/android/ftl/enum.h deleted file mode 100644 index e31c0a1322..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/enum.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -#include - -// Returns the name of enumerator E::V and optionally the class (i.e. "E::V" or "V") as -// std::optional by parsing the compiler-generated string literal for the -// signature of this function. The function is defined in the global namespace with a short name -// and inferred return type to reduce bloat in the read-only data segment. -template -constexpr auto ftl_enum_builder() { - static_assert(std::is_enum_v); - - using R = std::optional; - using namespace std::literals; - - // The "pretty" signature has the following format: - // - // auto ftl_enum() [E = android::test::Enum, V = android::test::Enum::kValue] - // - std::string_view view = __PRETTY_FUNCTION__; - const auto template_begin = view.rfind('['); - const auto template_end = view.rfind(']'); - if (template_begin == view.npos || template_end == view.npos) return R{}; - - // Extract the template parameters without the enclosing brackets. Example (cont'd): - // - // E = android::test::Enum, V = android::test::Enum::kValue - // - view = view.substr(template_begin + 1, template_end - template_begin - 1); - const auto value_begin = view.rfind("V = "sv); - if (value_begin == view.npos) return R{}; - - // Example (cont'd): - // - // V = android::test::Enum::kValue - // - view = view.substr(value_begin); - const auto pos = S ? view.rfind("::"sv) - 2 : view.npos; - - const auto name_begin = view.rfind("::"sv, pos); - if (name_begin == view.npos) return R{}; - - // Chop off the leading "::". - const auto name = view.substr(name_begin + 2); - - // A value that is not enumerated has the format "Enum)42". - return name.find(')') == view.npos ? R{name} : R{}; -} - -// Returns the name of enumerator E::V (i.e. "V") as std::optional -template -constexpr auto ftl_enum() { - return ftl_enum_builder(); -} - -// Returns the name of enumerator and class E::V (i.e. "E::V") as std::optional -template -constexpr auto ftl_enum_full() { - return ftl_enum_builder(); -} - -namespace android::ftl { - -// Trait for determining whether a type is specifically a scoped enum or not. By definition, a -// scoped enum is one that is not implicitly convertible to its underlying type. -// -// TODO: Replace with std::is_scoped_enum in C++23. -// - template> - struct is_scoped_enum : std::false_type { - }; - - template - struct is_scoped_enum - : std::negation>> { - }; - - template - inline constexpr bool is_scoped_enum_v = is_scoped_enum::value; - -// Shorthand for casting an enumerator to its integral value. -// -// TODO: Replace with std::to_underlying in C++23. -// -// enum class E { A, B, C }; -// static_assert(ftl::to_underlying(E::B) == 1); -// - template>> - constexpr auto to_underlying(E v) { - return static_cast>(v); - } - -// Traits for retrieving an enum's range. An enum specifies its range by defining enumerators named -// ftl_first and ftl_last. If omitted, ftl_first defaults to 0, whereas ftl_last defaults to N - 1 -// where N is the bit width of the underlying type, but only if that type is unsigned, assuming the -// enumerators are flags. Also, note that unscoped enums must define both bounds, as casting out-of- -// range values results in undefined behavior if the underlying type is not fixed. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_begin_v == E::A); -// static_assert(ftl::enum_last_v == E::F); -// static_assert(ftl::enum_size_v == 6); -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// static_assert(ftl::enum_begin_v == F{0}); -// static_assert(ftl::enum_last_v == F{15}); -// static_assert(ftl::enum_size_v == 16); -// - template - struct enum_begin { - static_assert(is_scoped_enum_v, "Missing ftl_first enumerator"); - static constexpr E value{0}; - }; - - template - struct enum_begin> { - static constexpr E value = E::ftl_first; - }; - - template - inline constexpr E enum_begin_v = enum_begin::value; - - template - struct enum_end { - using U = std::underlying_type_t; - static_assert(is_scoped_enum_v && std::is_unsigned_v, "Missing ftl_last enumerator"); - - static constexpr E value{std::numeric_limits::digits}; - }; - - template - struct enum_end> { - static constexpr E value = E{to_underlying(E::ftl_last) + 1}; - }; - - template - inline constexpr E enum_end_v = enum_end::value; - - template - inline constexpr E enum_last_v = E{to_underlying(enum_end_v) - 1}; - - template - struct enum_size { - static constexpr auto kBegin = to_underlying(enum_begin_v); - static constexpr auto kEnd = to_underlying(enum_end_v); - static_assert(kBegin < kEnd, "Invalid range"); - - static constexpr std::size_t value = kEnd - kBegin; - static_assert(value <= 64, "Excessive range size"); - }; - - template - inline constexpr std::size_t enum_size_v = enum_size::value; - - namespace details { - - template - struct Identity { - static constexpr auto value = V; - }; - - template - using make_enum_sequence = std::make_integer_sequence, enum_size_v>; - - template class = Identity, typename = make_enum_sequence> - struct EnumRange; - - template class F, typename T, T... Vs> - struct EnumRange> { - static constexpr auto kBegin = to_underlying(enum_begin_v); - static constexpr auto kSize = enum_size_v; - - using R = decltype(F::value); - const R values[kSize] = {F(Vs + kBegin)>::value...}; - - constexpr const auto *begin() const { return values; } - - constexpr const auto *end() const { return values + kSize; } - }; - - template - struct EnumName { - static constexpr auto value = ftl_enum(); - }; - - template - struct EnumNameFull { - static constexpr auto value = ftl_enum_full(); - }; - - template - struct FlagName { - using E = decltype(I); - using U = std::underlying_type_t; - - static constexpr E V{U{1} << to_underlying(I)}; - static constexpr auto value = ftl_enum(); - }; - - } // namespace details - -// Returns an iterable over the range of an enum. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// std::string string; -// for (E v : ftl::enum_range()) { -// string += ftl::enum_name(v).value_or("?"); -// } -// -// assert(string == "ABC??F"); -// - template - constexpr auto enum_range() { - return details::EnumRange{}; - } - -// Returns a stringified enumerator at compile time. -// -// enum class E { A, B, C }; -// static_assert(ftl::enum_name() == "B"); -// - template - constexpr std::string_view enum_name() { - constexpr auto kName = ftl_enum(); - static_assert(kName, "Unknown enumerator"); - return *kName; - } - -// Returns a stringified enumerator with class at compile time. -// -// enum class E { A, B, C }; -// static_assert(ftl::enum_name() == "E::B"); -// - template - constexpr std::string_view enum_name_full() { - constexpr auto kName = ftl_enum_full(); - static_assert(kName, "Unknown enumerator"); - return *kName; - } - -// Returns a stringified enumerator, possibly at compile time. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_name(E::C).value_or("?") == "C"); -// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); -// - template - constexpr std::optional enum_name(E v) { - const auto value = to_underlying(v); - - constexpr auto kBegin = to_underlying(enum_begin_v); - constexpr auto kLast = to_underlying(enum_last_v); - if (value < kBegin || value > kLast) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[value - kBegin]; - } - -// Returns a stringified enumerator with class, possibly at compile time. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// static_assert(ftl::enum_name(E::C).value_or("?") == "E::C"); -// static_assert(ftl::enum_name(E{3}).value_or("?") == "?"); -// - template - constexpr std::optional enum_name_full(E v) { - const auto value = to_underlying(v); - - constexpr auto kBegin = to_underlying(enum_begin_v); - constexpr auto kLast = to_underlying(enum_last_v); - if (value < kBegin || value > kLast) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[value - kBegin]; - } - -// Returns a stringified flag enumerator, possibly at compile time. -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// static_assert(ftl::flag_name(F::Z).value_or("?") == "Z"); -// static_assert(ftl::flag_name(F{0b111}).value_or("?") == "?"); -// - template - constexpr std::optional flag_name(E v) { - const auto value = to_underlying(v); - - // TODO: Replace with std::popcount and std::countr_zero in C++20. - if (__builtin_popcountll(value) != 1) return {}; - - constexpr auto kRange = details::EnumRange{}; - return kRange.values[__builtin_ctzll(value)]; - } - -// Returns a stringified enumerator, or its integral value if not named. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// assert(ftl::enum_string(E::C) == "C"); -// assert(ftl::enum_string(E{3}) == "3"); -// - template - inline std::string enum_string(E v) { - if (const auto name = enum_name(v)) { - return std::string(*name); - } - return to_string(to_underlying(v)); - } - -// Returns a stringified enumerator with class, or its integral value if not named. -// -// enum class E { A, B, C, F = 5, ftl_last = F }; -// -// assert(ftl::enum_string(E::C) == "E::C"); -// assert(ftl::enum_string(E{3}) == "3"); -// - template - inline std::string enum_string_full(E v) { - if (const auto name = enum_name_full(v)) { - return std::string(*name); - } - return to_string(to_underlying(v)); - } - -// Returns a stringified flag enumerator, or its integral value if not named. -// -// enum class F : std::uint16_t { X = 0b1, Y = 0b10, Z = 0b100 }; -// -// assert(ftl::flag_string(F::Z) == "Z"); -// assert(ftl::flag_string(F{7}) == "0b111"); -// - template - inline std::string flag_string(E v) { - if (const auto name = flag_name(v)) { - return std::string(*name); - } - constexpr auto radix = sizeof(E) == 1 ? Radix::kBin : Radix::kHex; - return to_string(to_underlying(v), radix); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/expected.h b/sysbridge/src/main/cpp/android/ftl/expected.h deleted file mode 100644 index 5ed362e536..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/expected.h +++ /dev/null @@ -1,144 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include "../libbase/expected.h" -#include -#include - -#include - -// Given an expression `expr` that evaluates to an ftl::Expected result (R for short), FTL_TRY -// unwraps T out of R, or bails out of the enclosing function F if R has an error E. The return type -// of F must be R, since FTL_TRY propagates R in the error case. As a special case, ftl::Unit may be -// used as the error E to allow FTL_TRY expressions when F returns `void`. -// -// The non-standard syntax requires `-Wno-gnu-statement-expression-from-macro-expansion` to compile. -// The UnitToVoid conversion allows the macro to be used for early exit from a function that returns -// `void`. -// -// Example usage: -// -// using StringExp = ftl::Expected; -// -// StringExp repeat(StringExp exp) { -// const std::string str = FTL_TRY(exp); -// return StringExp(str + str); -// } -// -// assert(StringExp("haha"s) == repeat(StringExp("ha"s))); -// assert(repeat(ftl::Unexpected(std::errc::bad_message)).has_error([](std::errc e) { -// return e == std::errc::bad_message; -// })); -// -// -// FTL_TRY may be used in void-returning functions by using ftl::Unit as the error type: -// -// void uppercase(char& c, ftl::Optional opt) { -// c = std::toupper(FTL_TRY(std::move(opt).ok_or(ftl::Unit()))); -// } -// -// char c = '?'; -// uppercase(c, std::nullopt); -// assert(c == '?'); -// -// uppercase(c, 'a'); -// assert(c == 'A'); -// -#define FTL_TRY(expr) \ - ({ \ - auto exp_ = (expr); \ - if (!exp_.has_value()) { \ - using E = decltype(exp_)::error_type; \ - return android::ftl::details::UnitToVoid::from(std::move(exp_)); \ - } \ - exp_.value(); \ - }) - -// Given an expression `expr` that evaluates to an ftl::Expected result (R for short), -// FTL_EXPECT unwraps T out of R, or bails out of the enclosing function F if R has an error E. -// While FTL_TRY bails out with R, FTL_EXPECT bails out with E, which is useful when F does not -// need to propagate R because T is not relevant to the caller. -// -// Example usage: -// -// using StringExp = ftl::Expected; -// -// std::errc repeat(StringExp exp, std::string& out) { -// const std::string str = FTL_EXPECT(exp); -// out = str + str; -// return std::errc::operation_in_progress; -// } -// -// std::string str; -// assert(std::errc::operation_in_progress == repeat(StringExp("ha"s), str)); -// assert("haha"s == str); -// assert(std::errc::bad_message == repeat(ftl::Unexpected(std::errc::bad_message), str)); -// assert("haha"s == str); -// -#define FTL_EXPECT(expr) \ - ({ \ - auto exp_ = (expr); \ - if (!exp_.has_value()) { \ - return std::move(exp_.error()); \ - } \ - exp_.value(); \ - }) - -namespace android::ftl { - -// Superset of base::expected with monadic operations. -// -// TODO: Extend std::expected in C++23. -// - template - struct Expected final : base::expected { - using Base = base::expected; - using Base::expected; - - using Base::error; - using Base::has_value; - using Base::value; - - template - constexpr bool has_error(P predicate) const { - return !has_value() && predicate(error()); - } - - constexpr Optional value_opt() const &{ - return has_value() ? Optional(value()) : std::nullopt; - } - - constexpr Optional value_opt() &&{ - return has_value() ? Optional(std::move(value())) : std::nullopt; - } - - // Delete new for this class. Its base doesn't have a virtual destructor, and - // if it got deleted via base class pointer, it would cause undefined - // behavior. There's not a good reason to allocate this object on the heap - // anyway. - static void *operator new(size_t) = delete; - - static void *operator new[](size_t) = delete; - }; - - template - constexpr auto Unexpected(E &&error) { - return base::unexpected(std::forward(error)); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/fake_guard.h b/sysbridge/src/main/cpp/android/ftl/fake_guard.h deleted file mode 100644 index 056564a1a1..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/fake_guard.h +++ /dev/null @@ -1,87 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#define FTL_ATTRIBUTE(a) __attribute__((a)) - -namespace android::ftl { - -// Granular alternative to [[clang::no_thread_safety_analysis]]. Given a std::mutex-like object, -// FakeGuard suppresses enforcement of thread-safe access to guarded variables within its scope. -// While FakeGuard is scoped to a block, there are macro shorthands for a single expression, as -// well as function/lambda scope (though calls must be indirect, e.g. virtual or std::function): -// -// struct { -// std::mutex mutex; -// int x FTL_ATTRIBUTE(guarded_by(mutex)) = -1; -// -// int f() { -// { -// ftl::FakeGuard guard(mutex); -// x = 0; -// } -// -// return FTL_FAKE_GUARD(mutex, x + 1); -// } -// -// std::function g() const { -// return [this]() FTL_FAKE_GUARD(mutex) { return x; }; -// } -// } s; -// -// assert(s.f() == 1); -// assert(s.g()() == 0); -// -// An example of a situation where FakeGuard helps is a mutex that guards writes on Thread 1, and -// reads on Thread 2. Reads on Thread 1, which is the only writer, need not be under lock, so can -// use FakeGuard to appease the thread safety analyzer. Another example is enforcing and documenting -// exclusive access by a single thread. This is done by defining a global constant that represents a -// thread context, and annotating guarded variables as if it were a mutex (though without any effect -// at run time): -// -// constexpr class [[clang::capability("mutex")]] { -// } kMainThreadContext; -// - template - struct [[clang::scoped_lockable]] FakeGuard final { - explicit FakeGuard(const Mutex &mutex) FTL_ATTRIBUTE(acquire_capability(mutex)) {} - - [[clang::release_capability()]] ~FakeGuard() {} - - FakeGuard(const FakeGuard &) = delete; - - FakeGuard &operator=(const FakeGuard &) = delete; - }; - -} // namespace android::ftl - -// TODO: Enable in C++23 once standard attributes can be used on lambdas. -#if 0 -#define FTL_FAKE_GUARD1(mutex) [[using clang: acquire_capability(mutex), release_capability(mutex)]] -#else -#define FTL_FAKE_GUARD1(mutex) \ - FTL_ATTRIBUTE(acquire_capability(mutex)) \ - FTL_ATTRIBUTE(release_capability(mutex)) -#endif - -#define FTL_FAKE_GUARD2(mutex, expr) \ - (android::ftl::FakeGuard(mutex), expr) - -#define FTL_MAKE_FAKE_GUARD(arg1, arg2, guard, ...) guard - -#define FTL_FAKE_GUARD(...) \ - FTL_MAKE_FAKE_GUARD(__VA_ARGS__, FTL_FAKE_GUARD2, FTL_FAKE_GUARD1, )(__VA_ARGS__) diff --git a/sysbridge/src/main/cpp/android/ftl/finalizer.h b/sysbridge/src/main/cpp/android/ftl/finalizer.h deleted file mode 100644 index b3342e959c..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/finalizer.h +++ /dev/null @@ -1,213 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -#include -#include -#include - -#include - -namespace android::ftl { - -// An RAII wrapper that invokes a function object as a finalizer when destroyed. -// -// The function object must take no arguments, and must return void. If the function object needs -// any context for the call, it must store it itself, for example with a lambda capture. -// -// The stored function object will be called once (unless canceled via the `cancel()` member -// function) at the first of: -// -// - The Finalizer instance is destroyed. -// - `operator()` is used to invoke the contained function. -// - The Finalizer instance is move-assigned a new value. The function being replaced will be -// invoked, and the replacement will be stored to be called later. -// -// The intent with this class is to keep cleanup code next to the code that requires that -// cleanup be performed. -// -// bool read_file(std::string filename) { -// FILE* f = fopen(filename.c_str(), "rb"); -// if (f == nullptr) return false; -// const auto cleanup = ftl::Finalizer([f]() { fclose(f); }); -// // fread(...), etc -// return true; -// } -// -// The `FinalFunction` template argument to Finalizer allows a polymorphic function -// type for storing the finalization function, such as `std::function` or `ftl::Function`. -// -// For convenience, this header defines a few useful aliases for using those types. -// -// - `FinalizerStd`, an alias for `Finalizer>` -// - `FinalizerFtl`, an alias for `Finalizer>` -// - `FinalizerFtl1`, an alias for `Finalizer>` -// - `FinalizerFtl2`, an alias for `Finalizer>` -// - `FinalizerFtl3`, an alias for `Finalizer>` -// -// Clients of this header are free to define other aliases they need. -// -// A Finalizer that uses a polymorphic function type can be returned from a function call and/or -// stored as member data (to be destroyed along with the containing class). -// -// auto register(Observer* observer) -> ftl::FinalizerStd { -// const auto id = observers.add(observer); -// return ftl::Finalizer([id]() { observers.remove(id); }); -// } -// -// { -// const auto _ = register(observer); -// // do the things that required the registered observer. -// } -// // the observer is removed. -// -// Cautions: -// -// 1. When a Finalizer is stored as member data, you will almost certainly want that cleanup to -// happen first, before the rest of the other member data is destroyed. For safety you should -// assume that the finalization function will access that data directly or indirectly. -// -// This means that Finalizers should be defined last, after all other normal member data in a -// class. -// -// class MyClass { -// public: -// bool initialize() { -// ready_ = true; -// cleanup_ = ftl::Finalizer([this]() { ready_ = false; }); -// return true; -// } -// -// bool ready_ = false; -// -// // Finalizers should be last so other class members can be accessed before being -// // destroyed. -// ftl::FinalizerStd cleanup_; -// }; -// -// 2. Care must be taken to use `ftl::Finalizer()` when constructing locally from a lambda. If you -// forget to do so, you are just creating a lambda that won't be automatically invoked! -// -// const auto bad = [&counter](){ ++counter; }; // Just a lambda instance -// const auto good = ftl::Finalizer([&counter](){ ++counter; }); -// - template - class Finalizer final { - // requires(std::is_invocable_r_v) - static_assert(std::is_invocable_r_v); - - public: - // A default constructed Finalizer does nothing when destroyed. - // requires(std::is_default_constructible_v) - constexpr Finalizer() = default; - - // Constructs a Finalizer from a function object. - // requires(std::is_invocable_v) - template>> - [[nodiscard]] explicit constexpr Finalizer(F &&function) - : Finalizer(std::forward(function), false) {} - - constexpr ~Finalizer() { maybe_invoke(); } - - // Disallow copying. - Finalizer(const Finalizer &that) = delete; - - auto operator=(const Finalizer &that) = delete; - - // Move construction - // requires(std::is_move_constructible_v) - [[nodiscard]] constexpr Finalizer(Finalizer &&that) - : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} - - // Implicit conversion move construction - // requires(!std::is_same_v>) - template>>> - // NOLINTNEXTLINE(google-explicit-constructor, cppcoreguidelines-rvalue-reference-param-not-moved) - [[nodiscard]] constexpr Finalizer(Finalizer &&that) - : Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)) {} - - // Move assignment - // requires(std::is_move_assignable_v) - constexpr auto operator=(Finalizer &&that) -> Finalizer & { - maybe_invoke(); - - function_ = std::move(that.function_); - canceled_ = std::exchange(that.canceled_, true); - - return *this; - } - - // Implicit conversion move assignment - // requires(!std::is_same_v>) - template>>> - // NOLINTNEXTLINE(cppcoreguidelines-rvalue-reference-param-not-moved) - constexpr auto operator=(Finalizer &&that) -> Finalizer & { - *this = Finalizer(std::move(that.function_), std::exchange(that.canceled_, true)); - return *this; - } - - // Cancels the final function, preventing it from being invoked. - constexpr void cancel() { - canceled_ = true; - maybe_nullify_function(); - } - - // Invokes the final function now, if not already invoked. - constexpr void operator()() { maybe_invoke(); } - - private: - template - friend - class Finalizer; - - template>> - [[nodiscard]] explicit constexpr Finalizer(F &&function, bool canceled) - : function_(std::forward(function)), canceled_(canceled) {} - - constexpr void maybe_invoke() { - if (!std::exchange(canceled_, true)) { - std::invoke(function_); - maybe_nullify_function(); - } - } - - constexpr void maybe_nullify_function() { - // Sets function_ to nullptr if that is supported for the backing type. - if constexpr (std::is_assignable_v) { - function_ = nullptr; - } - } - - FinalFunction function_; - bool canceled_ = true; - }; - - template - Finalizer(F &&) -> Finalizer>; - -// A standard alias for using `std::function` as the polymorphic function type. - using FinalizerStd = Finalizer>; - -// Helpful aliases for using `ftl::Function` as the polymorphic function type. - using FinalizerFtl = Finalizer>; - using FinalizerFtl1 = Finalizer>; - using FinalizerFtl2 = Finalizer>; - using FinalizerFtl3 = Finalizer>; - -} // namespace android::ftl \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/ftl/flags.h b/sysbridge/src/main/cpp/android/ftl/flags.h deleted file mode 100644 index 338acc240e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/flags.h +++ /dev/null @@ -1,246 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -// TODO(b/185536303): Align with FTL style. - -namespace android::ftl { - -/* A class for handling flags defined by an enum or enum class in a type-safe way. */ - template - class Flags { - // F must be an enum or its underlying type is undefined. Theoretically we could specialize this - // further to avoid this restriction but in general we want to encourage the use of enums - // anyways. - static_assert(std::is_enum_v, "Flags type must be an enum"); - using U = std::underlying_type_t; - - public: - constexpr Flags(F f) : mFlags(static_cast(f)) {} - - constexpr Flags(std::initializer_list fs) : mFlags(combine(fs)) {} - - constexpr Flags() : mFlags(0) {} - - constexpr Flags(const Flags &f) : mFlags(f.mFlags) {} - - // Provide a non-explicit construct for non-enum classes since they easily convert to their - // underlying types (e.g. when used with bitwise operators). For enum classes, however, we - // should force them to be explicitly constructed from their underlying types to make full use - // of the type checker. - template - constexpr Flags(T t, std::enable_if_t, T> * = nullptr) : mFlags(t) {} - - template - explicit constexpr Flags(T t, std::enable_if_t, T> * = nullptr) - : mFlags(t) {} - - class Iterator { - using Bits = std::uint64_t; - static_assert(sizeof(U) <= sizeof(Bits)); - - public: - constexpr Iterator() = default; - - Iterator(Flags flags) : mRemainingFlags(flags.mFlags) { (*this)++; } - - // Pre-fix ++ - Iterator &operator++() { - if (mRemainingFlags.none()) { - mCurrFlag = 0; - } else { - // TODO: Replace with std::countr_zero in C++20. - const Bits bit = static_cast(__builtin_ctzll( - mRemainingFlags.to_ullong())); - mRemainingFlags.reset(static_cast(bit)); - mCurrFlag = static_cast(static_cast(1) << bit); - } - return *this; - } - - // Post-fix ++ - Iterator operator++(int) { - Iterator iter = *this; - ++*this; - return iter; - } - - bool operator==(Iterator other) const { - return mCurrFlag == other.mCurrFlag && mRemainingFlags == other.mRemainingFlags; - } - - bool operator!=(Iterator other) const { return !(*this == other); } - - F operator*() const { return F{mCurrFlag}; } - - // iterator traits - - // In the future we could make this a bidirectional const iterator instead of a forward - // iterator but it doesn't seem worth the added complexity at this point. This could not, - // however, be made a non-const iterator as assigning one flag to another is a non-sensical - // operation. - using iterator_category = std::input_iterator_tag; - using value_type = F; - // Per the C++ spec, because input iterators are not assignable the iterator's reference - // type does not actually need to be a reference. In fact, making it a reference would imply - // that modifying it would change the underlying Flags object, which is obviously wrong for - // the same reason this can't be a non-const iterator. - using reference = F; - using difference_type = void; - using pointer = void; - - private: - std::bitset mRemainingFlags; - U mCurrFlag = 0; - }; - - /* - * Tests whether the given flag is set. - */ - bool test(F flag) const { - U f = static_cast(flag); - return (f & mFlags) == f; - } - - /* Tests whether any of the given flags are set */ - bool any(Flags f = ~Flags()) const { return (mFlags & f.mFlags) != 0; } - - /* Tests whether all of the given flags are set */ - bool all(Flags f) const { return (mFlags & f.mFlags) == f.mFlags; } - - constexpr Flags operator|(Flags rhs) const { - return static_cast(mFlags | rhs.mFlags); - } - - Flags &operator|=(Flags rhs) { - mFlags = mFlags | rhs.mFlags; - return *this; - } - - Flags operator&(Flags rhs) const { return static_cast(mFlags & rhs.mFlags); } - - Flags &operator&=(Flags rhs) { - mFlags = mFlags & rhs.mFlags; - return *this; - } - - Flags operator^(Flags rhs) const { return static_cast(mFlags ^ rhs.mFlags); } - - Flags &operator^=(Flags rhs) { - mFlags = mFlags ^ rhs.mFlags; - return *this; - } - - Flags operator~() { return static_cast(~mFlags); } - - bool operator==(Flags rhs) const { return mFlags == rhs.mFlags; } - - bool operator!=(Flags rhs) const { return !operator==(rhs); } - - Flags &operator=(const Flags &rhs) { - mFlags = rhs.mFlags; - return *this; - } - - inline Flags &clear(Flags f = static_cast(~static_cast(0))) { - return *this &= ~f; - } - - Iterator begin() const { return Iterator(*this); } - - Iterator end() const { return Iterator(); } - - /* - * Returns the stored set of flags. - * - * Note that this returns the underlying type rather than the base enum class. This is because - * the value is no longer necessarily a strict member of the enum since the returned value could - * be multiple enum variants OR'd together. - */ - U get() const { return mFlags; } - - std::string string() const { - std::string result; - bool first = true; - U unstringified = 0; - for (const F f: *this) { - if (const auto flagName = flag_name(f)) { - appendFlag(result, flagName.value(), first); - } else { - unstringified |= static_cast(f); - } - } - - if (unstringified != 0) { - constexpr auto radix = sizeof(U) == 1 ? Radix::kBin : Radix::kHex; - appendFlag(result, to_string(unstringified, radix), first); - } - - if (first) { - result += "0x0"; - } - - return result; - } - - private: - U mFlags; - - static constexpr U combine(std::initializer_list fs) { - U result = 0; - for (const F f: fs) { - result |= static_cast(f); - } - return result; - } - - static void appendFlag(std::string &str, const std::string_view &flag, bool &first) { - if (first) { - first = false; - } else { - str += " | "; - } - str += flag; - } - }; - -// This namespace provides operator overloads for enum classes to make it easier to work with them -// as flags. In order to use these, add them via a `using namespace` declaration. - namespace flag_operators { - - template>> - inline Flags operator~(F f) { - return static_cast(~to_underlying(f)); - } - - template>> - constexpr Flags operator|(F lhs, F rhs) { - return static_cast(to_underlying(lhs) | to_underlying(rhs)); - } - - } // namespace flag_operators -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/function.h b/sysbridge/src/main/cpp/android/ftl/function.h deleted file mode 100644 index 80caf4e743..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/function.h +++ /dev/null @@ -1,313 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace android::ftl { - -// ftl::Function is a container for function object, and can mostly be used in place of -// std::function. -// -// Unlike std::function, a ftl::Function: -// -// * Uses a static amount of memory (controlled by N), and never any dynamic allocation. -// * Satisfies the std::is_trivially_copyable<> trait. -// * Satisfies the std::is_trivially_destructible<> trait. -// -// However those same limits are also required from the contained function object in turn. -// -// The size of a ftl::Function is guaranteed to be: -// -// sizeof(std::intptr_t) * (N + 2) -// -// A ftl::Function can always be implicitly converted to a larger size ftl::Function. -// Trying to convert the other way leads to a compilation error. -// -// A default-constructed ftl::Function is in an empty state. The operator bool() overload returns -// false in this state. It is undefined behavior to attempt to invoke the function in this state. -// -// The ftl::Function can also be constructed or assigned from ftl::no_op. This sets up the -// ftl::Function to be non-empty, with a function that when called does nothing except -// default-constructs a return value. -// -// The ftl::make_function() helpers construct a ftl::Function, including deducing the -// values of F and N from the arguments it is given. -// -// The static ftl::Function::make() helpers construct a ftl::Function without that -// deduction, and also allow for implicit argument conversion if the target being called needs them. -// -// The construction helpers allow any of the following types of functions to be stored: -// -// * Any SMALL function object (as defined by the C++ Standard), such as a lambda with a small -// capture, or other "functor". The requirements are: -// -// 1) The function object must be trivial to destroy (in fact, the destructor will never -// actually be called once copied to the internal storage). -// 2) The function object must be trivial to copy (the raw bytes will be copied as the -// ftl::Function is copied/moved). -// 3) The size of the function object cannot be larger than sizeof(std::intptr_t) * (N + 1), -// and it cannot require stricter alignment than alignof(std::intptr_t). -// -// With the default of N=0, a lambda can only capture a single pointer-sized argument. This is -// enough to capture `this`, which is why N=0 is the default. -// -// * A member function, with the address passed as the template value argument to the construction -// helper function, along with the instance pointer needed to invoke it passed as an ordinary -// argument. -// -// ftl::make_function<&Class::member_function>(this); -// -// Note that the indicated member function will be invoked non-virtually. If you need it to be -// invoked virtually, you should invoke it yourself with a small lambda like so: -// -// ftl::function([this] { virtual_member_function(); }); -// -// * An ordinary function ("free function"), with the address of the function passed as a template -// value argument. -// -// ftl::make_function<&std::atoi>(); -// -// As with the member function helper, as the function is known at compile time, it will be called -// directly. -// -// Example usage: -// -// class MyClass { -// public: -// void on_event() const {} -// int on_string(int*, std::string_view) { return 1; } -// -// auto get_function() { -// return ftl::function([this] { on_event(); }); -// } -// } cls; -// -// // A function container with no arguments, and returning no value. -// ftl::Function f; -// -// // Construct a ftl::Function containing a small lambda. -// f = cls.get_function(); -// -// // Construct a ftl::Function that calls `cls.on_event()`. -// f = ftl::function<&MyClass::on_event>(&cls); -// -// // Create a do-nothing function. -// f = ftl::no_op; -// -// // Invoke the contained function. -// f(); -// -// // Also invokes it. -// std::invoke(f); -// -// // Create a typedef to give a more meaningful name and bound the size. -// using MyFunction = ftl::Function; -// int* ptr = nullptr; -// auto f1 = MyFunction::make( -// [cls = &cls, ptr](std::string_view sv) { -// return cls->on_string(ptr, sv); -// }); -// int r = f1("abc"sv); -// -// // Returns a default-constructed int (0). -// f1 = ftl::no_op; -// r = f1("abc"sv); -// assert(r == 0); - - template - class Function; - -// Used to construct a Function that does nothing. - struct NoOpTag { - }; - - constexpr NoOpTag no_op; - -// Detects that a type is a `ftl::Function` regardless of what `F` and `N` are. - template - struct is_function : public std::false_type { - }; - - template - struct is_function> : public std::true_type { - }; - - template - constexpr bool is_function_v = is_function::value; - - template - class Function final { - // Enforce a valid size, with an arbitrary maximum allowed size for the container of - // sizeof(std::intptr_t) * 16, though that maximum can be relaxed. - static_assert(N <= details::kFunctionMaximumN); - - using OpaqueStorageTraits = details::function_opaque_storage; - - public: - // Defining result_type allows ftl::Function to be substituted for std::function. - using result_type = Ret; - - // Constructs an empty ftl::Function. - Function() = default; - - // Constructing or assigning from nullptr_t also creates an empty ftl::Function. - Function(std::nullptr_t) {} - - Function &operator=(std::nullptr_t) { return *this = Function(nullptr); } - - // Constructing from NoOpTag sets up a a special no-op function which is valid to call, and which - // returns a default constructed return value. - Function(NoOpTag) : function_(details::bind_opaque_no_op()) {} - - Function &operator=(NoOpTag) { return *this = Function(no_op); } - - // Constructing/assigning from a function object stores a copy of that function object, however: - // * It must be trivially copyable, as the implementation makes a copy with memcpy(). - // * It must be trivially destructible, as the implementation doesn't destroy the copy! - // * It must fit in the limited internal storage, which enforces size/alignment restrictions. - - template>> - Function(const F &f) - : opaque_(OpaqueStorageTraits::opaque_copy(f)), - function_(details::bind_opaque_function_object(f)) {} - - template>> - Function &operator=(const F &f) noexcept { - return *this = Function{OpaqueStorageTraits::opaque_copy(f), - details::bind_opaque_function_object(f)}; - } - - // Constructing/assigning from a smaller ftl::Function is allowed, but not anything else. - - template - Function(const Function &other) - : opaque_{OpaqueStorageTraits::opaque_copy(other.opaque_)}, - function_(other.function_) {} - - template - auto &operator=(const Function &other) { - return *this = Function{OpaqueStorageTraits::opaque_copy(other.opaque_), - other.function_}; - } - - // Returns true if a function is set. - explicit operator bool() const { return function_ != nullptr; } - - // Checks if the other function has the same contents as this one. - bool operator==(const Function &other) const { - return other.opaque_ == opaque_ && other.function_ == function_; - } - - bool operator!=(const Function &other) const { return !operator==(other); } - - // Alternative way of testing for a function being set. - bool operator==(std::nullptr_t) const { return function_ == nullptr; } - - bool operator!=(std::nullptr_t) const { return function_ != nullptr; } - - // Invokes the function. - Ret operator()(Args... args) const { - return std::invoke(function_, opaque_.data(), std::forward(args)...); - } - - // Creation helper for function objects, such as lambdas. - template - static auto make(const F &f) -> decltype(Function{f}) { - return Function{f}; - } - - // Creation helper for a class pointer and a compile-time chosen member function to call. - template - static auto make(Class *instance) -> decltype(Function{ - details::bind_member_function(instance, - static_cast(nullptr))}) { - return Function{details::bind_member_function( - instance, static_cast(nullptr))}; - } - - // Creation helper for a compile-time chosen free function to call. - template - static auto make() -> decltype(Function{ - details::bind_free_function( - static_cast(nullptr))}) { - return Function{ - details::bind_free_function( - static_cast(nullptr))}; - } - - private: - // Needed so a Function can be converted to a Function. - template - friend - class Function; - - // The function pointer type of function stored in `function_`. The first argument is always - // `&opaque_`. - using StoredFunction = Ret(void *, Args...); - - // The type of the opaque storage, used to hold an appropriate function object. - // The type stored here is ONLY known to the StoredFunction. - // We always use at least one std::intptr_t worth of storage, and always a multiple of that size. - using OpaqueStorage = typename OpaqueStorageTraits::type; - - // Internal constructor for creating from a raw opaque blob + function pointer. - Function(const OpaqueStorage &opaque, StoredFunction *function) - : opaque_(opaque), function_(function) {} - - // Note: `mutable` so that `operator() const` can use it. - mutable OpaqueStorage opaque_{}; - StoredFunction *function_{nullptr}; - }; - -// Makes a ftl::Function given a function object `F`. - template> - Function(const F &) -> Function; - - template - auto make_function(const F &f) -> decltype(Function{f}) { - return Function{f}; - } - -// Makes a ftl::Function given a `MemberFunction` and a instance pointer to the associated `Class`. - template - auto make_function(Class *instance) - -> decltype(Function{details::bind_member_function( - instance, - static_cast *>(nullptr))}) { - return Function{details::bind_member_function( - instance, - static_cast *>(nullptr))}; - } - -// Makes a ftl::Function given an ordinary free function. - template - auto make_function() -> decltype(Function{ - details::bind_free_function( - static_cast(nullptr))}) { - return Function{ - details::bind_free_function( - static_cast(nullptr))}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/future.h b/sysbridge/src/main/cpp/android/ftl/future.h deleted file mode 100644 index 4110674618..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/future.h +++ /dev/null @@ -1,135 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -#include - -namespace android::ftl { - -// Thin wrapper around FutureImpl (concretely std::future or std::shared_future) with -// extensions for pure values (created via ftl::yield) and continuations. -// -// See also SharedFuture shorthand below. -// - template class FutureImpl = std::future> - class Future final : public details::BaseFuture, T, FutureImpl> { - using Base = details::BaseFuture; - - friend Base; // For BaseFuture<...>::self. - friend details::BaseFuture, T, std::future>; // For BaseFuture<...>::share. - - public: - // Constructs an invalid future. - Future() : future_(std::in_place_type>) {} - - // Constructs a future from its standard counterpart, implicitly. - Future(FutureImpl &&f) : future_(std::move(f)) {} - - bool valid() const { - return std::holds_alternative(future_) || std::get>(future_).valid(); - } - - // Forwarding functions. Base::share is only defined when FutureImpl is std::future, whereas the - // following are defined for either FutureImpl: - using Base::get; - using Base::wait_for; - - // Attaches a continuation to the future. The continuation is a function that maps T to either R - // or ftl::Future. In the former case, the chain wraps the result in a future as if by - // ftl::yield. - // - // auto future = ftl::yield(123); - // ftl::Future futures[] = {ftl::yield('a'), ftl::yield('b')}; - // - // auto chain = - // ftl::Future(std::move(future)) - // .then([](int x) { return static_cast(x % 2); }) - // .then([&futures](std::size_t i) { return std::move(futures[i]); }); - // - // assert(chain.get() == 'b'); - // - template> - auto then(F &&op) && -> Future> { - return defer( - [](auto &&f, F &&op) { - R r = op(f.get()); - if constexpr (std::is_same_v>) { - return r; - } else { - return r.get(); - } - }, - std::move(*this), std::forward(op)); - } - - private: - template - friend Future yield(V &&); - - template - friend Future yield(Args &&...); - - template - Future(details::ValueTag, Args &&... args) - : future_(std::in_place_type, std::forward(args)...) {} - - std::variant> future_; - }; - - template - using SharedFuture = Future; - -// Deduction guide for implicit conversion. - template class FutureImpl> - Future(FutureImpl &&) -> Future; - -// Creates a future that wraps a value. -// -// auto future = ftl::yield(42); -// assert(future.get() == 42); -// -// auto ptr = std::make_unique('!'); -// auto future = ftl::yield(std::move(ptr)); -// assert(*future.get() == '!'); -// - template - inline Future yield(V &&value) { - return {details::ValueTag{}, std::move(value)}; - } - - template - inline Future yield(Args &&... args) { - return {details::ValueTag{}, std::forward(args)...}; - } - -// Creates a future that defers a function call until its result is queried. -// -// auto future = ftl::defer([](int x) { return x + 1; }, 99); -// assert(future.get() == 100); -// - template - inline auto defer(F &&f, Args &&... args) { - return Future( - std::async(std::launch::deferred, std::forward(f), std::forward(args)...)); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/hash.h b/sysbridge/src/main/cpp/android/ftl/hash.h deleted file mode 100644 index 6d13672e7e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/hash.h +++ /dev/null @@ -1,44 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ftl { - -// Non-cryptographic hash function (namely CityHash64) for strings with at most 64 characters. -// Unlike std::hash, which returns std::size_t and is only required to produce the same result -// for the same input within a single execution of a program, this hash is stable. - inline std::optional stable_hash(std::string_view view) { - const auto length = view.length(); - if (length <= 16) { - return details::hash_length_0_to_16(view.data(), length); - } - if (length <= 32) { - return details::hash_length_17_to_32(view.data(), length); - } - if (length <= 64) { - return details::hash_length_33_to_64(view.data(), length); - } - return {}; - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/ignore.h b/sysbridge/src/main/cpp/android/ftl/ignore.h deleted file mode 100644 index bd33511747..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/ignore.h +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2025 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -namespace android::ftl { - -// An alternative to `std::ignore` that makes it easy to ignore multiple values. -// -// Examples: -// -// void ftl_ignore_multiple(int arg1, const char* arg2, std::string arg3) { -// // When invoked, all the arguments are ignored. -// ftl::ignore(arg1, arg2, arg3); -// } -// -// void ftl_ignore_single(int arg) { -// // It can be used like std::ignore to ignore a single value -// ftl::ignore = arg; -// } -// - inline constexpr struct { - // NOLINTNEXTLINE(misc-unconventional-assign-operator, readability-named-parameter) - constexpr auto operator=(auto &&) const -> decltype(*this) { return *this; } - - // NOLINTNEXTLINE(readability-named-parameter) - constexpr void operator()(auto &&...) const {} - } ignore; - -} // namespace android::ftl \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/ftl/initializer_list.h b/sysbridge/src/main/cpp/android/ftl/initializer_list.h deleted file mode 100644 index e6d15c3061..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/initializer_list.h +++ /dev/null @@ -1,112 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -namespace android::ftl { - -// Compile-time counterpart of std::initializer_list that stores per-element constructor -// arguments with heterogeneous types. For a container with elements of type T, given Sizes -// (S0, S1, ..., SN), N elements are initialized: the first element is initialized with the -// first S0 arguments, the second element is initialized with the next S1 arguments, and so -// on. The list of Types (T0, ..., TM) is flattened, so M is equal to the sum of the Sizes. -// -// An InitializerList is created using ftl::init::list, and is consumed by constructors of -// containers. The function call operator is overloaded such that arguments are accumulated -// in a tuple with each successive call. For instance, the following calls initialize three -// strings using different constructors, i.e. string literal, default, and count/character: -// -// ... = ftl::init::list("abc")()(3u, '?'); -// -// The following syntax is a shorthand for key-value pairs, where the first argument is the -// key, and the rest construct the value. The types of the key and value are deduced if the -// first pair contains exactly two arguments: -// -// ... = ftl::init::map(-1, "abc")(-2)(-3, 3u, '?'); -// -// ... = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); -// -// WARNING: The InitializerList returned by an ftl::init::list expression must be consumed -// immediately, since temporary arguments are destroyed after the full expression. Storing -// an InitializerList results in dangling references. -// - template, typename... Types> - struct InitializerList; - - template - struct InitializerList, Types...> { - // Creates a superset InitializerList by appending the number of arguments to Sizes, and - // expanding Types with forwarding references for each argument. - template - [[nodiscard]] constexpr auto operator()(Args &&... args) && -> InitializerList< - T, std::index_sequence, Types..., Args && ...> { - return {std::tuple_cat(std::move(tuple), - std::forward_as_tuple(std::forward(args)...))}; - } - - // The temporary InitializerList returned by operator() is bound to an rvalue reference in - // container constructors, which extends the lifetime of any temporary arguments that this - // tuple refers to until the completion of the full expression containing the construction. - std::tuple tuple; - }; - - template> - struct KeyValue { - }; - -// Shorthand for key-value pairs that assigns the first argument to the key, and the rest to the -// value. The specialization is on KeyValue rather than std::pair, so that ftl::init::list works -// with the latter. - template - struct InitializerList, std::index_sequence, Types...> { - // Accumulate the three arguments to std::pair's piecewise constructor. - template - [[nodiscard]] constexpr auto operator()(K &&k, Args &&... args) && -> InitializerList< - KeyValue, std::index_sequence, Types..., std::piecewise_construct_t, - std::tuple, std::tuple> { - return {std::tuple_cat( - std::move(tuple), - std::forward_as_tuple(std::piecewise_construct, - std::forward_as_tuple(std::forward(k)), - std::forward_as_tuple(std::forward(args)...)))}; - } - - std::tuple tuple; - }; - - namespace init { - - template - [[nodiscard]] constexpr auto list(Args &&... args) { - return InitializerList{}(std::forward(args)...); - } - - template, typename... Args> - [[nodiscard]] constexpr auto map(Args &&... args) { - return list>(std::forward(args)...); - } - - template - [[nodiscard]] constexpr auto map(K &&k, V &&v) { - return list>(std::forward(k), std::forward(v)); - } - - } // namespace init -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/match.h b/sysbridge/src/main/cpp/android/ftl/match.h deleted file mode 100644 index 490a4e72eb..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/match.h +++ /dev/null @@ -1,63 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include - -namespace android::ftl { - -// Concise alternative to std::visit that compiles to branches rather than a dispatch table. For -// std::variant where N is small, this is slightly faster since the branches can be -// inlined unlike the function pointers. -// -// using namespace std::chrono; -// std::variant duration = 119min; -// -// // Mutable match. -// ftl::match(duration, [](auto& d) { ++d; }); -// -// // Immutable match. Exhaustive due to minutes being convertible to seconds. -// assert("2 hours"s == -// ftl::match(duration, -// [](const seconds& s) { -// const auto h = duration_cast(s); -// return std::to_string(h.count()) + " hours"s; -// }, -// [](const hours& h) { return std::to_string(h.count() / 24) + " days"s; })); -// - template - decltype(auto) match(std::variant &variant, Ms &&... matchers) { - const auto matcher = details::Matcher{std::forward(matchers)...}; - static_assert(details::is_exhaustive_match_v, - "Non-exhaustive match"); - - return details::Match::match(variant, matcher); - } - - template - decltype(auto) match(const std::variant &variant, Ms &&... matchers) { - const auto matcher = details::Matcher{std::forward(matchers)...}; - static_assert(details::is_exhaustive_match_v, - "Non-exhaustive match"); - - return details::Match::match(variant, matcher); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/mixins.h b/sysbridge/src/main/cpp/android/ftl/mixins.h deleted file mode 100644 index 0cfae3ce27..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/mixins.h +++ /dev/null @@ -1,152 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// CRTP mixins for defining type-safe wrappers that are distinct from their underlying type. Common -// uses are IDs, opaque handles, and physical quantities. The constructor is provided by (and must -// be inherited from) the `Constructible` mixin, whereas operators (equality, ordering, arithmetic, -// etc.) are enabled through inheritance: -// -// struct Id : ftl::Constructible, ftl::Equatable { -// using Constructible::Constructible; -// }; -// -// static_assert(!std::is_default_constructible_v); -// -// Unlike `Constructible`, `DefaultConstructible` allows default construction. The default value is -// zero-initialized unless specified: -// -// struct Color : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Orderable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// static_assert(Color() == Color(0u)); -// static_assert(ftl::to_underlying(Color(-1)) == 255u); -// static_assert(Color(1u) < Color(2u)); -// -// struct Sequence : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Orderable, -// ftl::Incrementable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// static_assert(Sequence() == Sequence(-1)); -// -// The underlying type need not be a fundamental type: -// -// struct Timeout : ftl::DefaultConstructible, -// ftl::Equatable, -// ftl::Addable { -// using DefaultConstructible::DefaultConstructible; -// }; -// -// using namespace std::chrono_literals; -// static_assert(Timeout() + Timeout(5s) == Timeout(15s)); -// - template - struct Constructible { - explicit constexpr Constructible(T value) : value_(value) {} - - explicit constexpr operator const T &() const { return value_; } - - private: - template class> - friend - class details::Mixin; - - T value_; - }; - - template - struct DefaultConstructible : Constructible { - using Constructible::Constructible; - - constexpr DefaultConstructible() : DefaultConstructible(T{kDefault}) {} - }; - -// Shorthand for casting a type-safe wrapper to its underlying value. - template - constexpr const T &to_underlying(const Constructible &c) { - return static_cast(c); - } - -// Comparison operators for equality. - template - struct Equatable : details::Mixin { - constexpr bool operator==(const Self &other) const { - return to_underlying(this->self()) == to_underlying(other); - } - - constexpr bool operator!=(const Self &other) const { return !(*this == other); } - }; - -// Comparison operators for ordering. - template - struct Orderable : details::Mixin { - constexpr bool operator<(const Self &other) const { - return to_underlying(this->self()) < to_underlying(other); - } - - constexpr bool operator>(const Self &other) const { return other < this->self(); } - - constexpr bool operator>=(const Self &other) const { return !(*this < other); } - - constexpr bool operator<=(const Self &other) const { return !(*this > other); } - }; - -// Pre-increment and post-increment operators. - template - struct Incrementable : details::Mixin { - constexpr Self &operator++() { - ++this->mut(); - return this->self(); - } - - constexpr Self operator++(int) { - const Self tmp = this->self(); - operator++(); - return tmp; - } - }; - -// Additive operators, including incrementing. - template - struct Addable : details::Mixin, Incrementable { - constexpr Self &operator+=(const Self &other) { - this->mut() += to_underlying(other); - return this->self(); - } - - constexpr Self operator+(const Self &other) const { - Self tmp = this->self(); - return tmp += other; - } - - private: - using Base = details::Mixin; - using Base::mut; - using Base::self; - }; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/non_null.h b/sysbridge/src/main/cpp/android/ftl/non_null.h deleted file mode 100644 index df3e49dfd9..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/non_null.h +++ /dev/null @@ -1,228 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -namespace android::ftl { - -// Enforces and documents non-null pre/post-condition for (raw or smart) pointers. -// -// void get_length(const ftl::NonNull>& string_ptr, -// ftl::NonNull length_ptr) { -// // No need for `nullptr` checks. -// *length_ptr = string_ptr->length(); -// } -// -// const auto string_ptr = ftl::as_non_null(std::make_shared("android")); -// std::size_t size; -// get_length(string_ptr, ftl::as_non_null(&size)); -// assert(size == 7u); -// -// For compatibility with std::unique_ptr and performance with std::shared_ptr, move -// operations are allowed despite breaking the invariant: -// -// using Pair = std::pair>, std::shared_ptr>; -// -// Pair dupe_if(ftl::NonNull> non_null_ptr, bool condition) { -// // Move the underlying pointer out, so `non_null_ptr` must not be accessed after this point. -// auto unique_ptr = std::move(non_null_ptr).take(); -// -// auto non_null_shared_ptr = ftl::as_non_null(std::shared_ptr(std::move(unique_ptr))); -// auto nullable_shared_ptr = condition ? non_null_shared_ptr.get() : nullptr; -// -// return {std::move(non_null_shared_ptr), std::move(nullable_shared_ptr)}; -// } -// -// auto ptr = ftl::as_non_null(std::make_unique(42)); -// const auto [ptr1, ptr2] = dupe_if(std::move(ptr), true); -// assert(ptr1.get() == ptr2); -// - template - class NonNull final { - struct Passkey { - }; - - public: - // Disallow `nullptr` explicitly for clear compilation errors. - NonNull() = delete; - - NonNull(std::nullptr_t) = delete; - - // Copy operations. - - constexpr NonNull(const NonNull &) = default; - - constexpr NonNull &operator=(const NonNull &) = default; - - template>> - constexpr NonNull(const NonNull &other) : pointer_(other.get()) {} - - template>> - constexpr NonNull &operator=(const NonNull &other) { - pointer_ = other.get(); - return *this; - } - - [[nodiscard]] constexpr const Pointer &get() const { return pointer_; } - - [[nodiscard]] constexpr explicit operator const Pointer &() const { return get(); } - - // Move operations. These break the invariant, so care must be taken to avoid subsequent access. - - constexpr NonNull(NonNull &&) = default; - - constexpr NonNull &operator=(NonNull &&) = default; - - [[nodiscard]] constexpr Pointer take() &&{ return std::move(pointer_); } - - [[nodiscard]] constexpr explicit operator Pointer() && { return take(); } - - // Dereferencing. - [[nodiscard]] constexpr decltype(auto) operator*() const { return *get(); } - - [[nodiscard]] constexpr decltype(auto) operator->() const { return get(); } - - [[nodiscard]] constexpr explicit operator bool() const { return !(pointer_ == nullptr); } - - // Private constructor for ftl::as_non_null. Excluded from candidate constructors for conversions - // through the passkey idiom, for clear compilation errors. - template - constexpr NonNull(Passkey, P &&pointer) : pointer_(std::forward

(pointer)) { - if (pointer_ == nullptr) std::abort(); - } - - private: - template - friend constexpr auto as_non_null(P &&) -> NonNull>; - - Pointer pointer_; - }; - - template - [[nodiscard]] constexpr auto as_non_null(P &&pointer) -> NonNull> { - using Passkey = typename NonNull>::Passkey; - return {Passkey{}, std::forward

(pointer)}; - } - -// NonNull

<=> NonNull - - template - constexpr bool operator==(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() == rhs.get(); - } - - template - constexpr bool operator!=(const NonNull

&lhs, const NonNull &rhs) { - return !operator==(lhs, rhs); - } - - template - constexpr bool operator<(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() < rhs.get(); - } - - template - constexpr bool operator<=(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() <= rhs.get(); - } - - template - constexpr bool operator>=(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() >= rhs.get(); - } - - template - constexpr bool operator>(const NonNull

&lhs, const NonNull &rhs) { - return lhs.get() > rhs.get(); - } - -// NonNull

<=> Q - - template - constexpr bool operator==(const NonNull

&lhs, const Q &rhs) { - return lhs.get() == rhs; - } - - template - constexpr bool operator!=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() != rhs; - } - - template - constexpr bool operator<(const NonNull

&lhs, const Q &rhs) { - return lhs.get() < rhs; - } - - template - constexpr bool operator<=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() <= rhs; - } - - template - constexpr bool operator>=(const NonNull

&lhs, const Q &rhs) { - return lhs.get() >= rhs; - } - - template - constexpr bool operator>(const NonNull

&lhs, const Q &rhs) { - return lhs.get() > rhs; - } - -// P <=> NonNull - - template - constexpr bool operator==(const P &lhs, const NonNull &rhs) { - return lhs == rhs.get(); - } - - template - constexpr bool operator!=(const P &lhs, const NonNull &rhs) { - return lhs != rhs.get(); - } - - template - constexpr bool operator<(const P &lhs, const NonNull &rhs) { - return lhs < rhs.get(); - } - - template - constexpr bool operator<=(const P &lhs, const NonNull &rhs) { - return lhs <= rhs.get(); - } - - template - constexpr bool operator>=(const P &lhs, const NonNull &rhs) { - return lhs >= rhs.get(); - } - - template - constexpr bool operator>(const P &lhs, const NonNull &rhs) { - return lhs > rhs.get(); - } - -} // namespace android::ftl - -// Specialize std::hash for ftl::NonNull -template -struct std::hash> { - std::size_t operator()(const android::ftl::NonNull

&ptr) const { - return std::hash

()(ptr.get()); - } -}; diff --git a/sysbridge/src/main/cpp/android/ftl/optional.h b/sysbridge/src/main/cpp/android/ftl/optional.h deleted file mode 100644 index c7b5d26a67..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/optional.h +++ /dev/null @@ -1,147 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include "../libbase/expected.h" - -#include - -namespace android::ftl { - -// Superset of std::optional with monadic operations, as proposed in https://wg21.link/P0798R8. -// -// TODO: Remove standard APIs in C++23. -// - template - struct Optional final : std::optional { - using std::optional::optional; - - // Implicit downcast. - Optional(std::optional other) : std::optional(std::move(other)) {} - - using std::optional::has_value; - using std::optional::value; - - // Returns Optional where F is a function that maps T to U. - template - constexpr auto transform(F &&f) const &{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), value())); - return R(); - } - - template - constexpr auto transform(F &&f) &{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), value())); - return R(); - } - - template - constexpr auto transform(F &&f) const &&{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); - return R(); - } - - template - constexpr auto transform(F &&f) &&{ - using R = details::transform_result_t; - if (has_value()) return R(std::invoke(std::forward(f), std::move(value()))); - return R(); - } - - // Returns Optional where F is a function that maps T to Optional. - template - constexpr auto and_then(F &&f) const &{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), value()); - return R(); - } - - template - constexpr auto and_then(F &&f) &{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), value()); - return R(); - } - - template - constexpr auto and_then(F &&f) const &&{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), std::move(value())); - return R(); - } - - template - constexpr auto and_then(F &&f) &&{ - using R = details::and_then_result_t; - if (has_value()) return std::invoke(std::forward(f), std::move(value())); - return R(); - } - - // Returns this Optional if not nullopt, or else the Optional returned by the function F. - template - constexpr auto or_else(F &&f) const & -> details::or_else_result_t { - if (has_value()) return *this; - return std::forward(f)(); - } - - template - constexpr auto or_else(F &&f) && -> details::or_else_result_t { - if (has_value()) return std::move(*this); - return std::forward(f)(); - } - - // Maps this Optional to expected where nullopt becomes E. - template - constexpr auto ok_or(E &&e) && -> base::expected { - if (has_value()) return std::move(value()); - return base::unexpected(std::forward(e)); - } - - // Delete new for this class. Its base doesn't have a virtual destructor, and - // if it got deleted via base class pointer, it would cause undefined - // behavior. There's not a good reason to allocate this object on the heap - // anyway. - static void *operator new(size_t) = delete; - - static void *operator new[](size_t) = delete; - }; - - template - constexpr bool operator==(const Optional &lhs, const Optional &rhs) { - return static_cast>(lhs) == static_cast>(rhs); - } - - template - constexpr bool operator!=(const Optional &lhs, const Optional &rhs) { - return !(lhs == rhs); - } - -// Deduction guides. - template - Optional(T) -> Optional; - - template - Optional(std::optional) -> Optional; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/shared_mutex.h b/sysbridge/src/main/cpp/android/ftl/shared_mutex.h deleted file mode 100644 index 3008313038..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/shared_mutex.h +++ /dev/null @@ -1,49 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include - -namespace android::ftl { - -// Wrapper around std::shared_mutex to provide capabilities for thread-safety -// annotations. -// TODO(b/257958323): This class is no longer needed once b/135688034 is fixed (currently blocked on -// b/175635923). - class [[clang::capability("shared_mutex")]] SharedMutex final { - public: - [[clang::acquire_capability()]] void lock() { - mutex_.lock(); - } - - [[clang::release_capability()]] void unlock() { - mutex_.unlock(); - } - - [[clang::acquire_shared_capability()]] void lock_shared() { - mutex_.lock_shared(); - } - - [[clang::release_shared_capability()]] void unlock_shared() { - mutex_.unlock_shared(); - } - - private: - std::shared_mutex mutex_; - }; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/small_map.h b/sysbridge/src/main/cpp/android/ftl/small_map.h deleted file mode 100644 index f564909cef..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/small_map.h +++ /dev/null @@ -1,309 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include -#include -#include -#include - -namespace android::ftl { - -// Associative container with unique, unordered keys. Unlike std::unordered_map, key-value pairs are -// stored in contiguous storage for cache efficiency. The map is allocated statically until its size -// exceeds N, at which point mappings are relocated to dynamic memory. The try_emplace operation has -// a non-standard analogue try_replace that destructively emplaces. The API also defines an in-place -// counterpart to insert_or_assign: emplace_or_replace. Lookup is done not via a subscript operator, -// but immutable getters that can optionally transform the value. -// -// SmallMap unconditionally allocates on the heap. -// -// Example usage: -// -// ftl::SmallMap map; -// assert(map.empty()); -// assert(!map.dynamic()); -// -// map = ftl::init::map(123, "abc")(-1)(42, 3u, '?'); -// assert(map.size() == 3u); -// assert(!map.dynamic()); -// -// assert(map.contains(123)); -// assert(map.get(42).transform([](const std::string& s) { return s.size(); }) == 3u); -// -// const auto opt = map.get(-1); -// assert(opt); -// -// std::string& ref = *opt; -// assert(ref.empty()); -// ref = "xyz"; -// -// map.emplace_or_replace(0, "vanilla", 2u, 3u); -// assert(map.dynamic()); -// -// assert(map == SmallMap(ftl::init::map(-1, "xyz"sv)(0, "nil"sv)(42, "???"sv)(123, "abc"sv))); -// - template> - class SmallMap final { - using Map = SmallVector, N>; - - template - friend - class SmallMap; - - public: - using key_type = K; - using mapped_type = V; - - using value_type = typename Map::value_type; - using size_type = typename Map::size_type; - using difference_type = typename Map::difference_type; - - using reference = typename Map::reference; - using iterator = typename Map::iterator; - - using const_reference = typename Map::const_reference; - using const_iterator = typename Map::const_iterator; - - // Creates an empty map. - SmallMap() = default; - - // Constructs at most N key-value pairs in place by forwarding per-pair constructor arguments. - // The template arguments K, V, and N are inferred using the deduction guide defined below. - // The syntax for listing pairs is as follows: - // - // ftl::SmallMap map = ftl::init::map(123, "abc")(-1)(42, 3u, '?'); - // static_assert(std::is_same_v>); - // - // The types of the key and value are deduced if the first pair contains exactly two arguments: - // - // ftl::SmallMap map = ftl::init::map(0, 'a')(1, 'b')(2, 'c'); - // static_assert(std::is_same_v>); - // - template - SmallMap(InitializerList, Types...> &&list) - : map_(std::move(list)) { - deduplicate(); - } - - // Copies or moves key-value pairs from a convertible map. - template - SmallMap(SmallMap other) : map_(std::move(other.map_)) {} - - static constexpr size_type static_capacity() { return N; } - - size_type max_size() const { return map_.max_size(); } - - size_type size() const { return map_.size(); } - - bool empty() const { return map_.empty(); } - - // Returns whether the map is backed by static or dynamic storage. - bool dynamic() const { - if constexpr (static_capacity() > 0) { - return map_.dynamic(); - } else { - return true; - } - } - - iterator begin() { return map_.begin(); } - - const_iterator begin() const { return cbegin(); } - - const_iterator cbegin() const { return map_.cbegin(); } - - iterator end() { return map_.end(); } - - const_iterator end() const { return cend(); } - - const_iterator cend() const { return map_.cend(); } - - // Returns whether a mapping exists for the given key. - bool contains(const key_type &key) const { return get(key).has_value(); } - - // Returns a reference to the value for the given key, or std::nullopt if the key was not found. - // - // ftl::SmallMap map = ftl::init::map('a', 'A')('b', 'B')('c', 'C'); - // - // const auto opt = map.get('c'); - // assert(opt == 'C'); - // - // char d = 'd'; - // const auto ref = map.get('d').value_or(std::ref(d)); - // ref.get() = 'D'; - // assert(d == 'D'); - // - auto get(const key_type &key) const -> Optional> { - for (const auto &[k, v]: *this) { - if (KeyEqual{}(k, key)) { - return std::cref(v); - } - } - return {}; - } - - auto get(const key_type &key) -> Optional> { - for (auto &[k, v]: *this) { - if (KeyEqual{}(k, key)) { - return std::ref(v); - } - } - return {}; - } - - // Returns an iterator to an existing mapping for the given key, or the end() iterator otherwise. - const_iterator find(const key_type &key) const { - return const_cast(*this).find(key); - } - - iterator find(const key_type &key) { return find(key, begin()); } - - // Inserts a mapping unless it exists. Returns an iterator to the inserted or existing mapping, - // and whether the mapping was inserted. - // - // On emplace, if the map reaches its static or dynamic capacity, then all iterators are - // invalidated. Otherwise, only the end() iterator is invalidated. - // - template - std::pair try_emplace(const key_type &key, Args &&... args) { - if (const auto it = find(key); it != end()) { - return {it, false}; - } - - decltype(auto) ref_or_it = - map_.emplace_back(std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - - if constexpr (static_capacity() > 0) { - return {&ref_or_it, true}; - } else { - return {ref_or_it, true}; - } - } - - // Replaces a mapping if it exists, and returns an iterator to it. Returns the end() iterator - // otherwise. - // - // The value is replaced via move constructor, so type V does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the mapping being replaced. - // - // Iterators to the replaced mapping point to its replacement, and others remain valid. - // - template - iterator try_replace(const key_type &key, Args &&... args) { - const auto it = find(key); - if (it == end()) return it; - map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - return it; - } - - // In-place counterpart of std::unordered_map's insert_or_assign. Returns true on emplace, or - // false on replace. - // - // The value is emplaced and replaced via move constructor, so type V does not need to define - // copy/move assignment, e.g. its data members may be const. - // - // On emplace, if the map reaches its static or dynamic capacity, then all iterators are - // invalidated. Otherwise, only the end() iterator is invalidated. On replace, iterators - // to the replaced mapping point to its replacement, and others remain valid. - // - template - std::pair emplace_or_replace(const key_type &key, Args &&... args) { - const auto [it, ok] = try_emplace(key, std::forward(args)...); - if (ok) return {it, ok}; - map_.replace(it, std::piecewise_construct, std::forward_as_tuple(key), - std::forward_as_tuple(std::forward(args)...)); - return {it, ok}; - } - - // Removes a mapping if it exists, and returns whether it did. - // - // The last() and end() iterators, as well as those to the erased mapping, are invalidated. - // - bool erase(const key_type &key) { return erase(key, begin()); } - - // Removes a mapping. - // - // The last() and end() iterators, as well as those to the erased mapping, are invalidated. - // - void erase(iterator it) { map_.unstable_erase(it); } - - // Removes all mappings. - // - // All iterators are invalidated. - // - void clear() { map_.clear(); } - - private: - iterator find(const key_type &key, iterator first) { - return std::find_if(first, end(), - [&key](const auto &pair) { return KeyEqual{}(pair.first, key); }); - } - - bool erase(const key_type &key, iterator first) { - const auto it = find(key, first); - if (it == end()) return false; - map_.unstable_erase(it); - return true; - } - - void deduplicate() { - for (auto it = begin(); it != end();) { - if (const auto key = it->first; ++it != end()) { - while (erase(key, it)); - } - } - } - - Map map_; - }; - -// Deduction guide for in-place constructor. - template - SmallMap(InitializerList, std::index_sequence, Types...> &&) - -> SmallMap; - -// Returns whether the key-value pairs of two maps are equal. - template - bool operator==(const SmallMap &lhs, const SmallMap &rhs) { - if (lhs.size() != rhs.size()) return false; - - for (const auto &[k, v]: lhs) { - const auto &lv = v; - if (!rhs.get(k).transform([&lv](const W &rv) { return lv == rv; }).value_or(false)) { - return false; - } - } - - return true; - } - -// TODO: Remove in C++20. - template - inline bool operator!=(const SmallMap &lhs, const SmallMap &rhs) { - return !(lhs == rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/small_vector.h b/sysbridge/src/main/cpp/android/ftl/small_vector.h deleted file mode 100644 index ebbe63f16e..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/small_vector.h +++ /dev/null @@ -1,469 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include - -namespace android::ftl { - - template - struct is_small_vector; - -// ftl::StaticVector that promotes to std::vector when full. SmallVector is a drop-in replacement -// for std::vector with statically allocated storage for N elements, whose goal is to improve run -// time by avoiding heap allocation and increasing probability of cache hits. The standard API is -// augmented by an unstable_erase operation that does not preserve order, and a replace operation -// that destructively emplaces. -// -// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data -// members, or be const itself. -// -// SmallVector is a specialization that thinly wraps std::vector. -// -// Example usage: -// -// ftl::SmallVector vector; -// assert(vector.empty()); -// assert(!vector.dynamic()); -// -// vector = {'a', 'b', 'c'}; -// assert(vector.size() == 3u); -// assert(!vector.dynamic()); -// -// vector.push_back('d'); -// assert(vector.dynamic()); -// -// vector.unstable_erase(vector.begin()); -// assert(vector == (ftl::SmallVector{'d', 'b', 'c'})); -// -// vector.pop_back(); -// assert(vector.back() == 'b'); -// assert(vector.dynamic()); -// -// const char array[] = "hi"; -// vector = ftl::SmallVector(array); -// assert(vector == (ftl::SmallVector{'h', 'i', '\0'})); -// assert(!vector.dynamic()); -// -// ftl::SmallVector strings = ftl::init::list("abc")("123456", 3u)(3u, '?'); -// assert(strings.size() == 3u); -// assert(!strings.dynamic()); -// -// assert(strings[0] == "abc"); -// assert(strings[1] == "123"); -// assert(strings[2] == "???"); -// - template - class SmallVector final : details::ArrayTraits, details::ArrayComparators { - using Static = StaticVector; - using Dynamic = SmallVector; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // Creates an empty vector. - SmallVector() = default; - - // Constructs at most N elements. See StaticVector for underlying constructors. - template>{}>> - SmallVector(Arg &&arg, Args &&... args) - : vector_(std::in_place_type, std::forward(arg), - std::forward(args)...) {} - - // Copies or moves elements from a smaller convertible vector. - template 0)>> - SmallVector(SmallVector other) : vector_(convert(std::move(other))) {} - - void swap(SmallVector &other) { vector_.swap(other.vector_); } - - // Returns whether the vector is backed by static or dynamic storage. - bool dynamic() const { return std::holds_alternative(vector_); } - - // Avoid std::visit as it generates a dispatch table. -#define DISPATCH(T, F, ...) \ - T F() __VA_ARGS__ { \ - return dynamic() ? std::get(vector_).F() : std::get(vector_).F(); \ - } - - DISPATCH(size_type, max_size, const) - - DISPATCH(size_type, size, const) - - DISPATCH(bool, empty, const) - - DISPATCH(iterator, begin,) - - DISPATCH(const_iterator, begin, const) - - DISPATCH(const_iterator, cbegin, const) - - DISPATCH(iterator, end,) - - DISPATCH(const_iterator, end, const) - - DISPATCH(const_iterator, cend, const) - - DISPATCH(reverse_iterator, rbegin,) - - DISPATCH(const_reverse_iterator, rbegin, const) - - DISPATCH(const_reverse_iterator, crbegin, const) - - DISPATCH(reverse_iterator, rend,) - - DISPATCH(const_reverse_iterator, rend, const) - - DISPATCH(const_reverse_iterator, crend, const) - - DISPATCH(iterator, last,) - - DISPATCH(const_iterator, last, const) - - DISPATCH(reference, front,) - - DISPATCH(const_reference, front, const) - - DISPATCH(reference, back,) - - DISPATCH(const_reference, back, const) - - reference operator[](size_type i) { - return dynamic() ? std::get(vector_)[i] : std::get(vector_)[i]; - } - - const_reference - operator[](size_type i) const { return const_cast(*this)[i]; } - - // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so - // replacing at end() is erroneous. - // - // The element is emplaced via move constructor, so type T does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the element being replaced. - // - // Iterators to the replaced element point to its replacement, and others remain valid. - // - template - reference replace(const_iterator it, Args &&... args) { - if (dynamic()) { - return std::get(vector_).replace(it, std::forward(args)...); - } else { - return std::get(vector_).replace(it, std::forward(args)...); - } - } - - // Appends an element, and returns a reference to it. - // - // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. - // Otherwise, only the end() iterator is invalidated. - // - template - reference emplace_back(Args &&... args) { - constexpr auto kInsertStatic = &Static::template emplace_back; - constexpr auto kInsertDynamic = &Dynamic::template emplace_back; - return *insert(std::forward(args)...); - } - - // Appends an element. - // - // If the vector reaches its static or dynamic capacity, then all iterators are invalidated. - // Otherwise, only the end() iterator is invalidated. - // - void push_back(const value_type &v) { - constexpr auto kInsertStatic = - static_cast(&Static::push_back); - constexpr auto kInsertDynamic = - static_cast(&Dynamic::push_back); - insert(v); - } - - void push_back(value_type &&v) { - constexpr auto kInsertStatic = static_cast(&Static::push_back); - constexpr auto kInsertDynamic = - static_cast(&Dynamic::push_back); - insert(std::move(v)); - } - - // Removes the last element. The vector must not be empty, or the call is erroneous. - // - // The last() and end() iterators are invalidated. - // - DISPATCH(void, pop_back,) - - // Removes all elements. - // - // All iterators are invalidated. - // - DISPATCH(void, clear,) - -#undef DISPATCH - - // Erases an element, but does not preserve order. Rather than shifting subsequent elements, - // this moves the last element to the slot of the erased element. - // - // The last() and end() iterators, as well as those to the erased element, are invalidated. - // - void unstable_erase(iterator it) { - if (dynamic()) { - std::get(vector_).unstable_erase(it); - } else { - std::get(vector_).unstable_erase(it); - } - } - - // Extracts the elements as std::vector. - std::vector> promote() &&{ - if (dynamic()) { - return std::get(std::move(vector_)).promote(); - } else { - return {std::make_move_iterator(begin()), std::make_move_iterator(end())}; - } - } - - private: - template - friend - class SmallVector; - - template - static std::variant convert(SmallVector &&other) { - using Other = SmallVector; - - if (other.dynamic()) { - return std::get(std::move(other.vector_)); - } else { - return std::get(std::move(other.vector_)); - } - } - - template - auto insert(Args &&... args) { - if (Dynamic *const vector = std::get_if(&vector_)) { - return (vector->*InsertDynamic)(std::forward(args)...); - } - - auto &vector = std::get(vector_); - if (vector.full()) { - return (promote(vector).*InsertDynamic)(std::forward(args)...); - } else { - return (vector.*InsertStatic)(std::forward(args)...); - } - } - - Dynamic &promote(Static &static_vector) { - assert(static_vector.full()); - - // Allocate double capacity to reduce probability of reallocation. - Dynamic vector; - vector.reserve(Static::max_size() * 2); - std::move(static_vector.begin(), static_vector.end(), std::back_inserter(vector)); - - return vector_.template emplace(std::move(vector)); - } - - std::variant vector_; - }; - -// Partial specialization without static storage. - template - class SmallVector final : details::ArrayTraits, - details::ArrayComparators, - details::ArrayIterators, T>, - std::vector> { - using details::ArrayTraits::replace_at; - - using Iter = details::ArrayIterators; - using Impl = std::vector>; - - friend Iter; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // See std::vector for underlying constructors. - using Impl::Impl; - - // Copies and moves a vector, respectively. - SmallVector(const SmallVector &) = default; - - SmallVector(SmallVector &&) = default; - - // Constructs elements in place. See StaticVector for underlying constructor. - template - SmallVector(InitializerList, Types...> &&list) - : SmallVector(SmallVector(std::move(list))) {} - - // Copies or moves elements from a convertible vector. - template - SmallVector(SmallVector other) : Impl(convert(std::move(other))) {} - - SmallVector &operator=(SmallVector other) { - // Define copy/move assignment in terms of copy/move construction. - swap(other); - return *this; - } - - void swap(SmallVector &other) { Impl::swap(other); } - - using Impl::empty; - using Impl::max_size; - using Impl::size; - - using Impl::reserve; - - // std::vector iterators are not necessarily raw pointers. - iterator begin() { return Impl::data(); } - - iterator end() { return Impl::data() + size(); } - - using Iter::begin; - using Iter::end; - - using Iter::cbegin; - using Iter::cend; - - using Iter::rbegin; - using Iter::rend; - - using Iter::crbegin; - using Iter::crend; - - using Iter::last; - - using Iter::back; - using Iter::front; - - using Iter::operator[]; - - template - reference replace(const_iterator it, Args &&... args) { - return replace_at(it, std::forward(args)...); - } - - template - iterator emplace_back(Args &&... args) { - return &Impl::emplace_back(std::forward(args)...); - } - - bool push_back(const value_type &v) { - Impl::push_back(v); - return true; - } - - bool push_back(value_type &&v) { - Impl::push_back(std::move(v)); - return true; - } - - using Impl::clear; - using Impl::pop_back; - - void unstable_erase(iterator it) { - if (it != last()) replace(it, std::move(back())); - pop_back(); - } - - std::vector> promote() &&{ return std::move(*this); } - - private: - template - static Impl convert(SmallVector &&other) { - if constexpr (std::is_constructible_v> &&>) { - return std::move(other).promote(); - } else { - SmallVector vector(other.size()); - - // Consistently with StaticVector, T only requires copy/move construction from U, rather than - // copy/move assignment. - auto it = vector.begin(); - for (auto &element: other) { - vector.replace(it++, std::move(element)); - } - - return vector; - } - } - }; - - template - struct is_small_vector : std::false_type { - }; - - template - struct is_small_vector> : std::true_type { - }; - -// Deduction guide for array constructor. - template - SmallVector(T (&)[N]) -> SmallVector, N>; - -// Deduction guide for variadic constructor. - template, - typename = std::enable_if_t<(std::is_constructible_v && ...)>> - SmallVector(T &&, Us &&...) -> SmallVector; - -// Deduction guide for in-place constructor. - template - SmallVector(InitializerList, Types...> &&) - -> SmallVector; - -// Deduction guide for StaticVector conversion. - template - SmallVector(StaticVector &&) -> SmallVector; - - template - inline void swap(SmallVector &lhs, SmallVector &rhs) { - lhs.swap(rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/static_vector.h b/sysbridge/src/main/cpp/android/ftl/static_vector.h deleted file mode 100644 index d14c12b390..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/static_vector.h +++ /dev/null @@ -1,431 +0,0 @@ -/* - * Copyright 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include -#include - -namespace android::ftl { - - constexpr struct IteratorRangeTag { - } kIteratorRange; - -// Fixed-capacity, statically allocated counterpart of std::vector. Like std::array, StaticVector -// allocates contiguous storage for N elements of type T at compile time, but stores at most (rather -// than exactly) N elements. Unlike std::array, its default constructor does not require T to have a -// default constructor, since elements are constructed in place as the vector grows. Operations that -// insert an element (emplace_back, push_back, etc.) fail when the vector is full. The API otherwise -// adheres to standard containers, except the unstable_erase operation that does not preserve order, -// and the replace operation that destructively emplaces. -// -// Unlike std::vector, T does not require copy/move assignment, so may be an object with const data -// members, or be const itself. -// -// StaticVector is analogous to an iterable std::optional. -// StaticVector is an error. -// -// Example usage: -// -// ftl::StaticVector vector; -// assert(vector.empty()); -// -// vector = {'a', 'b'}; -// assert(vector.size() == 2u); -// -// vector.push_back('c'); -// assert(vector.full()); -// -// assert(!vector.push_back('d')); -// assert(vector.size() == 3u); -// -// vector.unstable_erase(vector.begin()); -// assert(vector == (ftl::StaticVector{'c', 'b'})); -// -// vector.pop_back(); -// assert(vector.back() == 'c'); -// -// const char array[] = "hi"; -// vector = ftl::StaticVector(array); -// assert(vector == (ftl::StaticVector{'h', 'i', '\0'})); -// -// ftl::StaticVector strings = ftl::init::list("abc")("123456", 3u)(3u, '?'); -// assert(strings.size() == 3u); -// assert(strings[0] == "abc"); -// assert(strings[1] == "123"); -// assert(strings[2] == "???"); -// - template - class StaticVector final : details::ArrayTraits, - details::ArrayIterators, T>, - details::ArrayComparators { - static_assert(N > 0); - - // For constructor that moves from a smaller convertible vector. - template - friend - class StaticVector; - - using details::ArrayTraits::construct_at; - using details::ArrayTraits::replace_at; - using details::ArrayTraits::in_place_swap_ranges; - using details::ArrayTraits::uninitialized_copy; - - using Iter = details::ArrayIterators; - friend Iter; - - // There is ambiguity when constructing from two iterator-like elements like pointers: - // they could be an iterator range, or arguments for in-place construction. Assume the - // latter unless they are input iterators and cannot be used to construct elements. If - // the former is intended, the caller can pass an IteratorRangeTag to disambiguate. - template> - using is_input_iterator = - std::conjunction, - std::negation>>; - - public: - FTL_ARRAY_TRAIT(T, value_type); - FTL_ARRAY_TRAIT(T, size_type); - FTL_ARRAY_TRAIT(T, difference_type); - - FTL_ARRAY_TRAIT(T, pointer); - FTL_ARRAY_TRAIT(T, reference); - FTL_ARRAY_TRAIT(T, iterator); - FTL_ARRAY_TRAIT(T, reverse_iterator); - - FTL_ARRAY_TRAIT(T, const_pointer); - FTL_ARRAY_TRAIT(T, const_reference); - FTL_ARRAY_TRAIT(T, const_iterator); - FTL_ARRAY_TRAIT(T, const_reverse_iterator); - - // Creates an empty vector. - StaticVector() = default; - - // Copies and moves a vector, respectively. - StaticVector(const StaticVector &other) - : StaticVector(kIteratorRange, other.begin(), other.end()) {} - - StaticVector(StaticVector &&other) { swap(other); } - - // Copies at most N elements from a smaller convertible vector. - template - StaticVector(const StaticVector &other) - : StaticVector(kIteratorRange, other.begin(), other.end()) { - static_assert(N >= M, "Insufficient capacity"); - } - - // Copies at most N elements from a smaller convertible array. - template - explicit StaticVector(U (&array)[M]) - : StaticVector(kIteratorRange, std::begin(array), std::end(array)) { - static_assert(N >= M, "Insufficient capacity"); - } - - // Copies at most N elements from the range [first, last). - // - // IteratorRangeTag disambiguates with initialization from two iterator-like elements. - // - template{}>> - StaticVector(Iterator first, Iterator last) : StaticVector(kIteratorRange, first, last) { - using V = typename std::iterator_traits::value_type; - static_assert(std::is_constructible_v, "Incompatible iterator range"); - } - - template - StaticVector(IteratorRangeTag, Iterator first, Iterator last) - : size_(std::min(max_size(), static_cast(std::distance(first, last)))) { - uninitialized_copy(first, first + size_, begin()); - } - - // Moves at most N elements from a smaller convertible vector. - template - StaticVector(StaticVector &&other) { - static_assert(N >= M, "Insufficient capacity"); - - // Same logic as swap, though M need not be equal to N. - std::uninitialized_move(other.begin(), other.end(), begin()); - std::destroy(other.begin(), other.end()); - std::swap(size_, other.size_); - } - - // Constructs at most N elements. The template arguments T and N are inferred using the - // deduction guide defined below. Note that T is determined from the first element, and - // subsequent elements must have convertible types: - // - // ftl::StaticVector vector = {1, 2, 3}; - // static_assert(std::is_same_v>); - // - // const auto copy = "quince"s; - // auto move = "tart"s; - // ftl::StaticVector vector = {copy, std::move(move)}; - // - // static_assert(std::is_same_v>); - // - template>> - StaticVector(E &&element, Es &&... elements) - : StaticVector(std::index_sequence<0>{}, std::forward(element), - std::forward(elements)...) { - static_assert(sizeof...(elements) < N, "Too many elements"); - } - - // Constructs at most N elements in place by forwarding per-element constructor arguments. The - // template arguments T and N are inferred using the deduction guide defined below. The syntax - // for listing arguments is as follows: - // - // ftl::StaticVector vector = ftl::init::list("abc")()(3u, '?'); - // - // static_assert(std::is_same_v>); - // assert(vector.full()); - // assert(vector[0] == "abc"); - // assert(vector[1].empty()); - // assert(vector[2] == "???"); - // - template - StaticVector(InitializerList, Types...> &&list) - : StaticVector(std::index_sequence<0, 0, Size>{}, std::make_index_sequence{}, - std::index_sequence{}, list.tuple) { - static_assert(sizeof...(Sizes) < N, "Too many elements"); - } - - ~StaticVector() { std::destroy(begin(), end()); } - - StaticVector &operator=(const StaticVector &other) { - StaticVector copy(other); - swap(copy); - return *this; - } - - StaticVector &operator=(StaticVector &&other) { - clear(); - swap(other); - return *this; - } - - // IsEmpty enables a fast path when the vector is known to be empty at compile time. - template - void swap(StaticVector &); - - static constexpr size_type max_size() { return N; } - - size_type size() const { return size_; } - - bool empty() const { return size() == 0; } - - bool full() const { return size() == max_size(); } - - iterator begin() { return std::launder(reinterpret_cast(data_)); } - - iterator end() { return begin() + size(); } - - using Iter::begin; - using Iter::end; - - using Iter::cbegin; - using Iter::cend; - - using Iter::rbegin; - using Iter::rend; - - using Iter::crbegin; - using Iter::crend; - - using Iter::last; - - using Iter::back; - using Iter::front; - - using Iter::operator[]; - - // Replaces an element, and returns a reference to it. The iterator must be dereferenceable, so - // replacing at end() is erroneous. - // - // The element is emplaced via move constructor, so type T does not need to define copy/move - // assignment, e.g. its data members may be const. - // - // The arguments may directly or indirectly refer to the element being replaced. - // - // Iterators to the replaced element point to its replacement, and others remain valid. - // - template - reference replace(const_iterator it, Args &&... args) { - return replace_at(it, std::forward(args)...); - } - - // Appends an element, and returns an iterator to it. If the vector is full, the element is not - // inserted, and the end() iterator is returned. - // - // On success, the end() iterator is invalidated. - // - template - iterator emplace_back(Args &&... args) { - if (full()) return end(); - const iterator it = construct_at(end(), std::forward(args)...); - ++size_; - return it; - } - - // Appends an element unless the vector is full, and returns whether the element was inserted. - // - // On success, the end() iterator is invalidated. - // - bool push_back(const value_type &v) { - // Two statements for sequence point. - const iterator it = emplace_back(v); - return it != end(); - } - - bool push_back(value_type &&v) { - // Two statements for sequence point. - const iterator it = emplace_back(std::move(v)); - return it != end(); - } - - // Removes the last element. The vector must not be empty, or the call is erroneous. - // - // The last() and end() iterators are invalidated. - // - void pop_back() { unstable_erase(last()); } - - // Removes all elements. - // - // All iterators are invalidated. - // - void clear() { - std::destroy(begin(), end()); - size_ = 0; - } - - // Erases an element, but does not preserve order. Rather than shifting subsequent elements, - // this moves the last element to the slot of the erased element. - // - // The last() and end() iterators, as well as those to the erased element, are invalidated. - // - void unstable_erase(const_iterator it) { - std::destroy_at(it); - if (it != last()) { - // Move last element and destroy its source for destructor side effects. This is only - // safe because exceptions are disabled. - construct_at(it, std::move(back())); - std::destroy_at(last()); - } - --size_; - } - - private: - // Recursion for variadic constructor. - template - StaticVector(std::index_sequence, E &&element, Es &&... elements) - : StaticVector(std::index_sequence{}, std::forward(elements)...) { - construct_at(begin() + I, std::forward(element)); - } - - // Base case for variadic constructor. - template - explicit StaticVector(std::index_sequence) : size_(I) {} - - // Recursion for in-place constructor. - // - // Construct element I by extracting its arguments from the InitializerList tuple. ArgIndex - // is the position of its first argument in Args, and ArgCount is the number of arguments. - // The Indices sequence corresponds to [0, ArgCount). - // - // The Sizes sequence lists the argument counts for elements after I, so Size is the ArgCount - // for the next element. The recursion stops when Sizes is empty for the last element. - // - template - StaticVector(std::index_sequence, std::index_sequence, - std::index_sequence, std::tuple &tuple) - : StaticVector(std::index_sequence{}, - std::make_index_sequence{}, std::index_sequence{}, - tuple) { - construct_at(begin() + I, std::move(std::get(tuple))...); - } - - // Base case for in-place constructor. - template - StaticVector(std::index_sequence, std::index_sequence, - std::index_sequence<>, std::tuple &tuple) - : size_(I + 1) { - construct_at(begin() + I, std::move(std::get(tuple))...); - } - - size_type size_ = 0; - std::aligned_storage_t data_[N]; - }; - -// Deduction guide for array constructor. - template - StaticVector(T (&)[N]) -> StaticVector, N>; - -// Deduction guide for variadic constructor. - template, - typename = std::enable_if_t<(std::is_constructible_v && ...)>> - StaticVector(T &&, Us &&...) -> StaticVector; - -// Deduction guide for in-place constructor. - template - StaticVector(InitializerList, Types...> &&) - -> StaticVector; - - template - template - void StaticVector::swap(StaticVector &other) { - auto [to, from] = std::make_pair(this, &other); - if (from == this) return; - - // Assume this vector has fewer elements, so the excess of the other vector will be moved to it. - auto [min, max] = std::make_pair(size(), other.size()); - - // No elements to swap if moving into an empty vector. - if constexpr (IsEmpty) { - assert(min == 0); - } else { - if (min > max) { - std::swap(from, to); - std::swap(min, max); - } - - // Swap elements [0, min). - in_place_swap_ranges(begin(), begin() + min, other.begin()); - - // No elements to move if sizes are equal. - if (min == max) return; - } - - // Move elements [min, max) and destroy their source for destructor side effects. - const auto [first, last] = std::make_pair(from->begin() + min, from->begin() + max); - std::uninitialized_move(first, last, to->begin() + min); - std::destroy(first, last); - - std::swap(size_, other.size_); - } - - template - inline void swap(StaticVector &lhs, StaticVector &rhs) { - lhs.swap(rhs); - } - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/string.h b/sysbridge/src/main/cpp/android/ftl/string.h deleted file mode 100644 index 46e45e8169..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/string.h +++ /dev/null @@ -1,105 +0,0 @@ -/* - * Copyright 2021 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include -#include - -namespace android::ftl { - - enum class Radix { - kBin = 2, kDec = 10, kHex = 16 - }; - - template - struct to_chars_length { - static_assert(std::is_integral_v); - // Maximum binary digits, plus minus sign and radix prefix. - static constexpr std::size_t value = - std::numeric_limits>::digits + 3; - }; - - template - constexpr std::size_t to_chars_length_v = to_chars_length::value; - - template - using to_chars_buffer_t = char[to_chars_length_v]; - -// Lightweight (not allocating nor sprintf-based) alternative to std::to_string for integers, with -// optional radix. See also ftl::to_string below. -// -// ftl::to_chars_buffer_t<> buffer; -// -// assert(ftl::to_chars(buffer, 123u) == "123"); -// assert(ftl::to_chars(buffer, -42, ftl::Radix::kBin) == "-0b101010"); -// assert(ftl::to_chars(buffer, 0xcafe, ftl::Radix::kHex) == "0xcafe"); -// assert(ftl::to_chars(buffer, '*', ftl::Radix::kHex) == "0x2a"); -// - template - std::string_view to_chars(char (&buffer)[N], T v, Radix radix = Radix::kDec) { - static_assert(N >= to_chars_length_v); - - auto begin = buffer + 2; - const auto [end, err] = std::to_chars(begin, buffer + N, v, static_cast(radix)); - assert(err == std::errc()); - - if (radix == Radix::kDec) { - // TODO: Replace with {begin, end} in C++20. - return {begin, static_cast(end - begin)}; - } - - const auto prefix = radix == Radix::kBin ? 'b' : 'x'; - if constexpr (std::is_unsigned_v) { - buffer[0] = '0'; - buffer[1] = prefix; - } else { - if (*begin == '-') { - *buffer = '-'; - } else { - --begin; - } - - *begin-- = prefix; - *begin = '0'; - } - - // TODO: Replace with {buffer, end} in C++20. - return {buffer, static_cast(end - buffer)}; - } - -// Lightweight (not sprintf-based) alternative to std::to_string for integers, with optional radix. -// -// assert(ftl::to_string(123u) == "123"); -// assert(ftl::to_string(-42, ftl::Radix::kBin) == "-0b101010"); -// assert(ftl::to_string(0xcafe, ftl::Radix::kHex) == "0xcafe"); -// assert(ftl::to_string('*', ftl::Radix::kHex) == "0x2a"); -// - template - inline std::string to_string(T v, Radix radix = Radix::kDec) { - to_chars_buffer_t buffer; - return std::string(to_chars(buffer, v, radix)); - } - - std::string to_string(bool) = delete; - - std::string to_string(bool, Radix) = delete; - -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/ftl/unit.h b/sysbridge/src/main/cpp/android/ftl/unit.h deleted file mode 100644 index 2dd71e43a1..0000000000 --- a/sysbridge/src/main/cpp/android/ftl/unit.h +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android::ftl { - -// The unit type, and its only value. - constexpr struct Unit { - } unit; - - constexpr bool operator==(Unit, Unit) { - return true; - } - - constexpr bool operator!=(Unit, Unit) { - return false; - } - -// Adapts a function object F to return Unit. The return value of F is ignored. -// -// As a practical use, the function passed to ftl::Optional::transform is not allowed to return -// void (cf. https://wg21.link/P0798R8#mapping-functions-returning-void), but may return Unit if -// only its side effects are meaningful: -// -// ftl::Optional opt = "food"s; -// opt.transform(ftl::unit_fn([](std::string& str) { str.pop_back(); })); -// assert(opt == "foo"s); -// - template - struct UnitFn { - F f; - - template - Unit operator()(Args &&... args) { - return f(std::forward(args)...), unit; - } - }; - - template - constexpr auto unit_fn(F &&f) -> UnitFn> { - return {std::forward(f)}; - } - - namespace details { - -// Identity function for all T except Unit, which maps to void. - template - struct UnitToVoid { - template - static auto from(U &&value) { - return value; - } - }; - - template<> - struct UnitToVoid { - template - static void from(U &&) {} - }; - - } // namespace details -} // namespace android::ftl diff --git a/sysbridge/src/main/cpp/android/input/Input.cpp b/sysbridge/src/main/cpp/android/input/Input.cpp deleted file mode 100644 index 7af317a488..0000000000 --- a/sysbridge/src/main/cpp/android/input/Input.cpp +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include - -#define LOG_TAG "Input" - -namespace android { - - bool isFromSource(uint32_t source, uint32_t test) { - return (source & test) == test; - } -} \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/Input.h b/sysbridge/src/main/cpp/android/input/Input.h deleted file mode 100644 index b07479be2a..0000000000 --- a/sysbridge/src/main/cpp/android/input/Input.h +++ /dev/null @@ -1,71 +0,0 @@ - -/* - * Flags that flow alongside events in the input dispatch system to help with certain - * policy decisions such as waking from device sleep. - * - * These flags are also defined in frameworks/base/core/java/android/view/WindowManagerPolicy.java. - */ -enum { - /* These flags originate in RawEvents and are generally set in the key map. - * NOTE: If you want a flag to be able to set in a keylayout file, then you must add it to - * InputEventLabels.h as well. */ - - // Indicates that the event should wake the device. - POLICY_FLAG_WAKE = 0x00000001, - - // Indicates that the key is virtual, such as a capacitive button, and should - // generate haptic feedback. Virtual keys may be suppressed for some time - // after a recent touch to prevent accidental activation of virtual keys adjacent - // to the touch screen during an edge swipe. - POLICY_FLAG_VIRTUAL = 0x00000002, - - // Indicates that the key is the special function modifier. - POLICY_FLAG_FUNCTION = 0x00000004, - - // Indicates that the key represents a special gesture that has been detected by - // the touch firmware or driver. Causes touch events from the same device to be canceled. - // This policy flag prevents key events from changing touch mode state. - POLICY_FLAG_GESTURE = 0x00000008, - - // Indicates that key usage mapping represents a fallback mapping. - // Fallback mappings cannot be used to definitively determine whether a device - // supports a key code. For example, a HID device can report a key press - // as a HID usage code if it is not mapped to any linux key code in the kernel. - // However, we cannot know which HID usage codes that device supports from - // userspace through the evdev. We can use fallback mappings to convert HID - // usage codes to Android key codes without needing to know if a device can - // actually report the usage code. - POLICY_FLAG_FALLBACK_USAGE_MAPPING = 0x00000010, - - POLICY_FLAG_RAW_MASK = 0x0000ffff, - - /* These flags are set by the input dispatcher. */ - - // Indicates that the input event was injected. - POLICY_FLAG_INJECTED = 0x01000000, - - // Indicates that the input event is from a trusted source such as a directly attached - // input device or an application with system-wide event injection permission. - POLICY_FLAG_TRUSTED = 0x02000000, - - // Indicates that the input event has passed through an input filter. - POLICY_FLAG_FILTERED = 0x04000000, - - // Disables automatic key repeating behavior. - POLICY_FLAG_DISABLE_KEY_REPEAT = 0x08000000, - - /* These flags are set by the input reader policy as it intercepts each event. */ - - // Indicates that the device was in an interactive state when the - // event was intercepted. - POLICY_FLAG_INTERACTIVE = 0x20000000, - - // Indicates that the event should be dispatched to applications. - // The input event should still be sent to the InputDispatcher so that it can see all - // input events received include those that it will not deliver. - POLICY_FLAG_PASS_TO_USER = 0x40000000, -}; - -namespace android { - bool isFromSource(uint32_t source, uint32_t test); -} \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.cpp b/sysbridge/src/main/cpp/android/input/InputDevice.cpp deleted file mode 100644 index 66c72d20a7..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputDevice.cpp +++ /dev/null @@ -1,358 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - - -#include -#include -#include - -#include "../logging.h" -#include "../libbase/stringprintf.h" -#include -#include "InputDevice.h" -#include "InputEventLabels.h" -#include "../ui/LogicalDisplayId.h" - -using android::base::StringPrintf; - -namespace android { - -// Set to true to log detailed debugging messages about IDC file probing. - static constexpr bool DEBUG_PROBE = false; - - static const char *CONFIGURATION_FILE_DIR[] = { - "idc/", - "keylayout/", - "keychars/", - }; - - static const char *CONFIGURATION_FILE_EXTENSION[] = { - ".idc", - ".kl", - ".kcm", - }; - - static bool isValidNameChar(char ch) { - return isascii(ch) && (isdigit(ch) || isalpha(ch) || ch == '-' || ch == '_'); - } - - static void appendInputDeviceConfigurationFileRelativePath(std::string &path, - const std::string &name, - InputDeviceConfigurationFileType type) { - path += CONFIGURATION_FILE_DIR[static_cast(type)]; - path += name; - path += CONFIGURATION_FILE_EXTENSION[static_cast(type)]; - } - - std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier &deviceIdentifier, InputDeviceConfigurationFileType type, - const char *suffix) { - if (deviceIdentifier.vendor != 0 && deviceIdentifier.product != 0) { - if (deviceIdentifier.version != 0) { - // Try vendor product version. - std::string versionPath = - getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%" - "04x_Version_%04x%s", - deviceIdentifier.vendor, - deviceIdentifier.product, - deviceIdentifier.version, - suffix), - type); - if (!versionPath.empty()) { - LOGI("Found key layout map by version path %s", versionPath.c_str()); - return versionPath; - } - } - - // Try vendor product. - std::string productPath = - getInputDeviceConfigurationFilePathByName( - StringPrintf("Vendor_%04x_Product_%04x%s", - deviceIdentifier.vendor, - deviceIdentifier.product, - suffix), - type); - if (!productPath.empty()) { - LOGI("Found key layout map by product path %s", productPath.c_str()); - return productPath; - } - } - - // Try device name. - std::string namePath = getInputDeviceConfigurationFilePathByName( - deviceIdentifier.getCanonicalName() + suffix, - type); - - if (!namePath.empty()) { - LOGI("Found key layout map by name path %s", namePath.c_str()); - return namePath; - } - - // As a last resort, just use the Generic file. - return getInputDeviceConfigurationFilePathByName("Generic", type); - } - - std::string getInputDeviceConfigurationFilePathByName( - const std::string &name, InputDeviceConfigurationFileType type) { - // Search system repository. - std::string path; - - // Treblized input device config files will be located /product/usr, /system_ext/usr, - // /odm/usr or /vendor/usr. - std::vector pathPrefixes{ - "/product/usr/", - "/system_ext/usr/", - "/odm/usr/", - "/vendor/usr/", - "/system/usr", - }; - // These files may also be in the APEX pointed by input_device.config_file.apex sysprop. -// if (auto apex = GetProperty("input_device.config_file.apex", ""); !apex.empty()) { -// pathPrefixes.push_back("/apex/" + apex + "/etc/usr/"); -// } - // ANDROID_ROOT may not be set on host - if (auto android_root = getenv("ANDROID_ROOT"); android_root != nullptr) { - pathPrefixes.push_back(std::string(android_root) + "/usr/"); - } - for (const auto &prefix: pathPrefixes) { - path = prefix; - appendInputDeviceConfigurationFileRelativePath(path, name, type); - if (!access(path.c_str(), R_OK)) { - if (DEBUG_PROBE) { - LOGI("Found system-provided input device configuration file at %s", - path.c_str()); - } - return path; - } else if (errno != ENOENT) { - if (DEBUG_PROBE) { - LOGW("Couldn't find a system-provided input device configuration file at %s due to error %d (%s); there may be an IDC file there that cannot be loaded.", - path.c_str(), errno, strerror(errno)); - } - } else { - if (DEBUG_PROBE) { - LOGE("Didn't find system-provided input device configuration file at %s: %s", - path.c_str(), strerror(errno)); - } - } - } - - // Search user repository. - // TODO Should only look here if not in safe mode. - path = ""; - char *androidData = getenv("ANDROID_DATA"); - if (androidData != nullptr) { - path += androidData; - } - path += "/system/devices/"; - appendInputDeviceConfigurationFileRelativePath(path, name, type); - if (!access(path.c_str(), R_OK)) { - if (DEBUG_PROBE) { - LOGI("Found system user input device configuration file at %s", path.c_str()); - } - return path; - } else if (errno != ENOENT) { - LOGW("Couldn't find a system user input device configuration file at %s due to error %d (%s); there may be an IDC file there that cannot be loaded.", - path.c_str(), errno, strerror(errno)); - } else { - if (DEBUG_PROBE) { - LOGE("Didn't find system user input device configuration file at %s: %s", - path.c_str(), strerror(errno)); - } - } - - // Not found. - if (DEBUG_PROBE) { - LOGI("Probe failed to find input device configuration file with name '%s' and type %s", - name.c_str(), ftl::enum_string(type).c_str()); - } - - - return ""; - } - -// --- InputDeviceIdentifier - - std::string InputDeviceIdentifier::getCanonicalName() const { - std::string replacedName = name; - for (char &ch: replacedName) { - if (!isValidNameChar(ch)) { - ch = '_'; - } - } - return replacedName; - } - - -// --- InputDeviceInfo --- - - InputDeviceInfo::InputDeviceInfo() { - initialize(-1, 0, -1, InputDeviceIdentifier(), "", false, false, - ui::LogicalDisplayId::INVALID); - } - - InputDeviceInfo::InputDeviceInfo(const InputDeviceInfo &other) - : mId(other.mId), - mGeneration(other.mGeneration), - mControllerNumber(other.mControllerNumber), - mIdentifier(other.mIdentifier), - mAlias(other.mAlias), - mIsExternal(other.mIsExternal), - mHasMic(other.mHasMic), - mKeyboardLayoutInfo(other.mKeyboardLayoutInfo), - mSources(other.mSources), - mKeyboardType(other.mKeyboardType), - mUsiVersion(other.mUsiVersion), - mAssociatedDisplayId(other.mAssociatedDisplayId), - mEnabled(other.mEnabled), - mHasVibrator(other.mHasVibrator), - mHasBattery(other.mHasBattery), - mHasButtonUnderPad(other.mHasButtonUnderPad), - mHasSensor(other.mHasSensor), - mMotionRanges(other.mMotionRanges), - mSensors(other.mSensors), - mLights(other.mLights), - mViewBehavior(other.mViewBehavior) {} - - InputDeviceInfo &InputDeviceInfo::operator=(const InputDeviceInfo &other) { - mId = other.mId; - mGeneration = other.mGeneration; - mControllerNumber = other.mControllerNumber; - mIdentifier = other.mIdentifier; - mAlias = other.mAlias; - mIsExternal = other.mIsExternal; - mHasMic = other.mHasMic; - mKeyboardLayoutInfo = other.mKeyboardLayoutInfo; - mSources = other.mSources; - mKeyboardType = other.mKeyboardType; - mUsiVersion = other.mUsiVersion; - mAssociatedDisplayId = other.mAssociatedDisplayId; - mEnabled = other.mEnabled; - mHasVibrator = other.mHasVibrator; - mHasBattery = other.mHasBattery; - mHasButtonUnderPad = other.mHasButtonUnderPad; - mHasSensor = other.mHasSensor; - mMotionRanges = other.mMotionRanges; - mSensors = other.mSensors; - mLights = other.mLights; - mViewBehavior = other.mViewBehavior; - return *this; - } - - InputDeviceInfo::~InputDeviceInfo() { - } - - void InputDeviceInfo::initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier &identifier, - const std::string &alias, - bool isExternal, bool hasMic, - ui::LogicalDisplayId associatedDisplayId, - InputDeviceViewBehavior viewBehavior, bool enabled) { - mId = id; - mGeneration = generation; - mControllerNumber = controllerNumber; - mIdentifier = identifier; - mAlias = alias; - mIsExternal = isExternal; - mHasMic = hasMic; - mSources = 0; - mKeyboardType = AINPUT_KEYBOARD_TYPE_NONE; - mAssociatedDisplayId = associatedDisplayId; - mEnabled = enabled; - mHasVibrator = false; - mHasBattery = false; - mHasButtonUnderPad = false; - mHasSensor = false; - mViewBehavior = viewBehavior; - mUsiVersion.reset(); - mMotionRanges.clear(); - mSensors.clear(); - mLights.clear(); - } - - const InputDeviceInfo::MotionRange *InputDeviceInfo::getMotionRange( - int32_t axis, uint32_t source) const { - for (const MotionRange &range: mMotionRanges) { - if (range.axis == axis && isFromSource(range.source, source)) { - return ⦥ - } - } - return nullptr; - } - - void InputDeviceInfo::addSource(uint32_t source) { - mSources |= source; - } - - void InputDeviceInfo::addMotionRange(int32_t axis, uint32_t source, float min, float max, - float flat, float fuzz, float resolution) { - MotionRange range = {axis, source, min, max, flat, fuzz, resolution}; - mMotionRanges.push_back(range); - } - - void InputDeviceInfo::addMotionRange(const MotionRange &range) { - mMotionRanges.push_back(range); - } - - void InputDeviceInfo::addSensorInfo(const InputDeviceSensorInfo &info) { - if (mSensors.find(info.type) != mSensors.end()) { - LOGW("Sensor type %s already exists, will be replaced by new sensor added.", - ftl::enum_string(info.type).c_str()); - } - mSensors.insert_or_assign(info.type, info); - } - - void InputDeviceInfo::addBatteryInfo(const InputDeviceBatteryInfo &info) { - if (mBatteries.find(info.id) != mBatteries.end()) { - LOGW("Battery id %d already exists, will be replaced by new battery added.", info.id); - } - mBatteries.insert_or_assign(info.id, info); - } - - void InputDeviceInfo::addLightInfo(const InputDeviceLightInfo &info) { - if (mLights.find(info.id) != mLights.end()) { - LOGW("Light id %d already exists, will be replaced by new light added.", info.id); - } - mLights.insert_or_assign(info.id, info); - } - - void InputDeviceInfo::setKeyboardType(int32_t keyboardType) { - mKeyboardType = keyboardType; - } - - void InputDeviceInfo::setKeyboardLayoutInfo(KeyboardLayoutInfo layoutInfo) { - mKeyboardLayoutInfo = std::move(layoutInfo); - } - - std::vector InputDeviceInfo::getSensors() { - std::vector infos; - infos.reserve(mSensors.size()); - for (const auto &[type, info]: mSensors) { - infos.push_back(info); - } - return infos; - } - - std::vector InputDeviceInfo::getLights() { - std::vector infos; - infos.reserve(mLights.size()); - for (const auto &[id, info]: mLights) { - infos.push_back(info); - } - return infos; - } - -} // namespace android \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/input/InputDevice.h b/sysbridge/src/main/cpp/android/input/InputDevice.h deleted file mode 100644 index c7af07cc20..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputDevice.h +++ /dev/null @@ -1,455 +0,0 @@ -/* - * Copyright (C) 2012 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include "../ftl/flags.h" -#include "../ftl/mixins.h" -#include "Input.h" -#include -#include -#include -#include "../ui/LogicalDisplayId.h" - -namespace android { - -/* - * Identifies a device. - */ - struct InputDeviceIdentifier { - inline InputDeviceIdentifier() : - bus(0), vendor(0), product(0), version(0) { - } - - // Information provided by the kernel. - std::string name; - std::string location; - std::string uniqueId; - int bus; - int vendor; - int product; - int version; - - // A composite input device descriptor string that uniquely identifies the device - // even across reboots or reconnections. The value of this field is used by - // upper layers of the input system to associate settings with individual devices. - // It is hashed from whatever kernel provided information is available. - // Ideally, the way this value is computed should not change between Android releases - // because that would invalidate persistent settings that rely on it. - std::string descriptor; - - // A value added to uniquely identify a device in the absence of a unique id. This - // is intended to be a minimum way to distinguish from other active devices and may - // reuse values that are not associated with an input anymore. - uint16_t nonce; - - // The bluetooth address of the device, if known. - std::optional bluetoothAddress; - - /** - * Return InputDeviceIdentifier.name that has been adjusted as follows: - * - all characters besides alphanumerics, dash, - * and underscore have been replaced with underscores. - * This helps in situations where a file that matches the device name is needed, - * while conforming to the filename limitations. - */ - std::string getCanonicalName() const; - - bool operator==(const InputDeviceIdentifier &) const = default; - - bool operator!=(const InputDeviceIdentifier &) const = default; - }; - -/** - * Holds View related behaviors for an InputDevice. - */ - struct InputDeviceViewBehavior { - /** - * The smooth scroll behavior that applies for all source/axis, if defined by the device. - * Empty optional if the device has not specified the default smooth scroll behavior. - */ - std::optional shouldSmoothScroll; - }; - -/* Types of input device sensors. Keep sync with core/java/android/hardware/Sensor.java */ - enum class InputDeviceSensorType : int32_t { - ACCELEROMETER = ASENSOR_TYPE_ACCELEROMETER, - MAGNETIC_FIELD = ASENSOR_TYPE_MAGNETIC_FIELD, - ORIENTATION = 3, - GYROSCOPE = ASENSOR_TYPE_GYROSCOPE, - LIGHT = ASENSOR_TYPE_LIGHT, - PRESSURE = ASENSOR_TYPE_PRESSURE, - TEMPERATURE = 7, - PROXIMITY = ASENSOR_TYPE_PROXIMITY, - GRAVITY = ASENSOR_TYPE_GRAVITY, - LINEAR_ACCELERATION = ASENSOR_TYPE_LINEAR_ACCELERATION, - ROTATION_VECTOR = ASENSOR_TYPE_ROTATION_VECTOR, - RELATIVE_HUMIDITY = ASENSOR_TYPE_RELATIVE_HUMIDITY, - AMBIENT_TEMPERATURE = ASENSOR_TYPE_AMBIENT_TEMPERATURE, - MAGNETIC_FIELD_UNCALIBRATED = ASENSOR_TYPE_MAGNETIC_FIELD_UNCALIBRATED, - GAME_ROTATION_VECTOR = ASENSOR_TYPE_GAME_ROTATION_VECTOR, - GYROSCOPE_UNCALIBRATED = ASENSOR_TYPE_GYROSCOPE_UNCALIBRATED, - SIGNIFICANT_MOTION = ASENSOR_TYPE_SIGNIFICANT_MOTION, - - ftl_first = ACCELEROMETER, - ftl_last = SIGNIFICANT_MOTION - }; - - enum class InputDeviceSensorAccuracy : int32_t { - NONE = 0, - LOW = 1, - MEDIUM = 2, - HIGH = 3, - - ftl_last = HIGH, - }; - - enum class InputDeviceSensorReportingMode : int32_t { - CONTINUOUS = 0, - ON_CHANGE = 1, - ONE_SHOT = 2, - SPECIAL_TRIGGER = 3, - }; - - enum class InputDeviceLightType : int32_t { - INPUT = 0, - PLAYER_ID = 1, - KEYBOARD_BACKLIGHT = 2, - KEYBOARD_MIC_MUTE = 3, - KEYBOARD_VOLUME_MUTE = 4, - - ftl_last = KEYBOARD_VOLUME_MUTE - }; - - enum class InputDeviceLightCapability : uint32_t { - /** Capability to change brightness of the light */ - BRIGHTNESS = 0x00000001, - /** Capability to change color of the light */ - RGB = 0x00000002, - }; - - struct InputDeviceSensorInfo { - explicit InputDeviceSensorInfo(std::string name, std::string vendor, int32_t version, - InputDeviceSensorType type, - InputDeviceSensorAccuracy accuracy, - float maxRange, float resolution, float power, - int32_t minDelay, - int32_t fifoReservedEventCount, int32_t fifoMaxEventCount, - std::string stringType, int32_t maxDelay, int32_t flags, - int32_t id) - : name(name), - vendor(vendor), - version(version), - type(type), - accuracy(accuracy), - maxRange(maxRange), - resolution(resolution), - power(power), - minDelay(minDelay), - fifoReservedEventCount(fifoReservedEventCount), - fifoMaxEventCount(fifoMaxEventCount), - stringType(stringType), - maxDelay(maxDelay), - flags(flags), - id(id) {} - - // Name string of the sensor. - std::string name; - // Vendor string of this sensor. - std::string vendor; - // Version of the sensor's module. - int32_t version; - // Generic type of this sensor. - InputDeviceSensorType type; - // The current accuracy of sensor event. - InputDeviceSensorAccuracy accuracy; - // Maximum range of the sensor in the sensor's unit. - float maxRange; - // Resolution of the sensor in the sensor's unit. - float resolution; - // The power in mA used by this sensor while in use. - float power; - // The minimum delay allowed between two events in microsecond or zero if this sensor only - // returns a value when the data it's measuring changes. - int32_t minDelay; - // Number of events reserved for this sensor in the batch mode FIFO. - int32_t fifoReservedEventCount; - // Maximum number of events of this sensor that could be batched. - int32_t fifoMaxEventCount; - // The type of this sensor as a string. - std::string stringType; - // The delay between two sensor events corresponding to the lowest frequency that this sensor - // supports. - int32_t maxDelay; - // Sensor flags - int32_t flags; - // Sensor id, same as the input device ID it belongs to. - int32_t id; - }; - - struct BrightnessLevel : ftl::DefaultConstructible, - ftl::Equatable, - ftl::Orderable, - ftl::Addable { - using DefaultConstructible::DefaultConstructible; - }; - - struct InputDeviceLightInfo { - explicit InputDeviceLightInfo(std::string name, int32_t id, InputDeviceLightType type, - ftl::Flags capabilityFlags, - int32_t ordinal, - std::set preferredBrightnessLevels) - : name(name), - id(id), - type(type), - capabilityFlags(capabilityFlags), - ordinal(ordinal), - preferredBrightnessLevels(std::move(preferredBrightnessLevels)) {} - - // Name string of the light. - std::string name; - // Light id - int32_t id; - // Type of the light. - InputDeviceLightType type; - // Light capabilities. - ftl::Flags capabilityFlags; - // Ordinal of the light - int32_t ordinal; - // Custom brightness levels for the light - std::set preferredBrightnessLevels; - }; - - struct InputDeviceBatteryInfo { - explicit InputDeviceBatteryInfo(std::string name, int32_t id) : name(name), id(id) {} - - // Name string of the battery. - std::string name; - // Battery id - int32_t id; - }; - - struct KeyboardLayoutInfo { - explicit KeyboardLayoutInfo(std::string languageTag, std::string layoutType) - : languageTag(languageTag), layoutType(layoutType) {} - - // A BCP 47 conformant language tag such as "en-US". - std::string languageTag; - // The layout type such as QWERTY or AZERTY. - std::string layoutType; - - inline bool operator==(const KeyboardLayoutInfo &other) const { - return languageTag == other.languageTag && layoutType == other.layoutType; - } - - inline bool operator!=(const KeyboardLayoutInfo &other) const { return !(*this == other); } - }; - -// The version of the Universal Stylus Initiative (USI) protocol supported by the input device. - struct InputDeviceUsiVersion { - int32_t majorVersion = -1; - int32_t minorVersion = -1; - }; - -/* - * Describes the characteristics and capabilities of an input device. - */ - class InputDeviceInfo { - public: - InputDeviceInfo(); - - InputDeviceInfo(const InputDeviceInfo &other); - - InputDeviceInfo &operator=(const InputDeviceInfo &other); - - ~InputDeviceInfo(); - - struct MotionRange { - int32_t axis; - uint32_t source; - float min; - float max; - float flat; - float fuzz; - float resolution; - }; - - void initialize(int32_t id, int32_t generation, int32_t controllerNumber, - const InputDeviceIdentifier &identifier, const std::string &alias, - bool isExternal, bool hasMic, ui::LogicalDisplayId associatedDisplayId, - InputDeviceViewBehavior viewBehavior = {{}}, bool enabled = true); - - inline int32_t getId() const { return mId; } - - inline int32_t getControllerNumber() const { return mControllerNumber; } - - inline int32_t getGeneration() const { return mGeneration; } - - inline const InputDeviceIdentifier &getIdentifier() const { return mIdentifier; } - - inline const std::string &getAlias() const { return mAlias; } - - inline const std::string &getDisplayName() const { - return mAlias.empty() ? mIdentifier.name : mAlias; - } - - inline bool isExternal() const { return mIsExternal; } - - inline bool hasMic() const { return mHasMic; } - - inline uint32_t getSources() const { return mSources; } - - const MotionRange *getMotionRange(int32_t axis, uint32_t source) const; - - void addSource(uint32_t source); - - void addMotionRange(int32_t axis, uint32_t source, - float min, float max, float flat, float fuzz, float resolution); - - void addMotionRange(const MotionRange &range); - - void addSensorInfo(const InputDeviceSensorInfo &info); - - void addBatteryInfo(const InputDeviceBatteryInfo &info); - - void addLightInfo(const InputDeviceLightInfo &info); - - void setKeyboardType(int32_t keyboardType); - - inline int32_t getKeyboardType() const { return mKeyboardType; } - - void setKeyboardLayoutInfo(KeyboardLayoutInfo keyboardLayoutInfo); - - inline const std::optional &getKeyboardLayoutInfo() const { - return mKeyboardLayoutInfo; - } - - inline const InputDeviceViewBehavior &getViewBehavior() const { return mViewBehavior; } - - inline void setVibrator(bool hasVibrator) { mHasVibrator = hasVibrator; } - - inline bool hasVibrator() const { return mHasVibrator; } - - inline void setHasBattery(bool hasBattery) { mHasBattery = hasBattery; } - - inline bool hasBattery() const { return mHasBattery; } - - inline void setButtonUnderPad(bool hasButton) { mHasButtonUnderPad = hasButton; } - - inline bool hasButtonUnderPad() const { return mHasButtonUnderPad; } - - inline void setHasSensor(bool hasSensor) { mHasSensor = hasSensor; } - - inline bool hasSensor() const { return mHasSensor; } - - inline const std::vector &getMotionRanges() const { - return mMotionRanges; - } - - std::vector getSensors(); - - std::vector getLights(); - - inline void setUsiVersion(std::optional usiVersion) { - mUsiVersion = std::move(usiVersion); - } - - inline std::optional getUsiVersion() const { return mUsiVersion; } - - inline ui::LogicalDisplayId getAssociatedDisplayId() const { return mAssociatedDisplayId; } - - inline void setEnabled(bool enabled) { mEnabled = enabled; } - - inline bool isEnabled() const { return mEnabled; } - - private: - int32_t mId; - int32_t mGeneration; - int32_t mControllerNumber; - InputDeviceIdentifier mIdentifier; - std::string mAlias; - bool mIsExternal; - bool mHasMic; - std::optional mKeyboardLayoutInfo; - uint32_t mSources; - int32_t mKeyboardType; - std::optional mUsiVersion; - ui::LogicalDisplayId mAssociatedDisplayId{ui::LogicalDisplayId::INVALID}; - bool mEnabled; - - bool mHasVibrator; - bool mHasBattery; - bool mHasButtonUnderPad; - bool mHasSensor; - - std::vector mMotionRanges; - std::unordered_map mSensors; - /* Map from light ID to light info */ - std::unordered_map mLights; - /* Map from battery ID to battery info */ - std::unordered_map mBatteries; - /** The View related behaviors for the device. */ - InputDeviceViewBehavior mViewBehavior; - }; - -/* Types of input device configuration files. */ - enum class InputDeviceConfigurationFileType : int32_t { - CONFIGURATION = 0, /* .idc file */ - KEY_LAYOUT = 1, /* .kl file */ - KEY_CHARACTER_MAP = 2, /* .kcm file */ - ftl_last = KEY_CHARACTER_MAP, - }; - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * The optional suffix is appended to the end of the file name (before the - * extension). - * - * The device identifier is used to construct several default configuration file - * names to try based on the device name, vendor, product, and version. - * - * Returns an empty string if not found. - */ - extern std::string getInputDeviceConfigurationFilePathByDeviceIdentifier( - const InputDeviceIdentifier &deviceIdentifier, InputDeviceConfigurationFileType type, - const char *suffix = ""); - -/* - * Gets the path of an input device configuration file, if one is available. - * Considers both system provided and user installed configuration files. - * - * The name is case-sensitive and is used to construct the filename to resolve. - * All characters except 'a'-'z', 'A'-'Z', '0'-'9', '-', and '_' are replaced by underscores. - * - * Returns an empty string if not found. - */ - extern std::string getInputDeviceConfigurationFilePathByName( - const std::string &name, InputDeviceConfigurationFileType type); - - enum ReservedInputDeviceId : int32_t { - // Device id representing an invalid device - INVALID_INPUT_DEVICE_ID = -2, - // Device id of a special "virtual" keyboard that is always present. - VIRTUAL_KEYBOARD_ID = -1, - // Device id of the "built-in" keyboard if there is one. - BUILT_IN_KEYBOARD_ID = 0, - // First device id available for dynamic devices - END_RESERVED_ID = 1, - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp b/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp deleted file mode 100644 index d8d3c5b0ca..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputEventLabels.cpp +++ /dev/null @@ -1,1422 +0,0 @@ -/* - * Copyright (C) 2020 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "InputEventLabels.h" - -#include -#include -#include -#include "Input.h" - -#define DEFINE_KEYCODE(key) { #key, AKEYCODE_##key } -#define DEFINE_AXIS(axis) { #axis, AMOTION_EVENT_AXIS_##axis } -#define DEFINE_FLAG(flag) { #flag, POLICY_FLAG_##flag } - -namespace android { - -// clang-format off - -// NOTE: If you add a new keycode here you must also add it to several other files. -// Refer to frameworks/base/core/java/android/view/KeyEvent.java for the full list. -#define KEYCODES_SEQUENCE \ - DEFINE_KEYCODE(UNKNOWN), \ - DEFINE_KEYCODE(SOFT_LEFT), \ - DEFINE_KEYCODE(SOFT_RIGHT), \ - DEFINE_KEYCODE(HOME), \ - DEFINE_KEYCODE(BACK), \ - DEFINE_KEYCODE(CALL), \ - DEFINE_KEYCODE(ENDCALL), \ - DEFINE_KEYCODE(0), \ - DEFINE_KEYCODE(1), \ - DEFINE_KEYCODE(2), \ - DEFINE_KEYCODE(3), \ - DEFINE_KEYCODE(4), \ - DEFINE_KEYCODE(5), \ - DEFINE_KEYCODE(6), \ - DEFINE_KEYCODE(7), \ - DEFINE_KEYCODE(8), \ - DEFINE_KEYCODE(9), \ - DEFINE_KEYCODE(STAR), \ - DEFINE_KEYCODE(POUND), \ - DEFINE_KEYCODE(DPAD_UP), \ - DEFINE_KEYCODE(DPAD_DOWN), \ - DEFINE_KEYCODE(DPAD_LEFT), \ - DEFINE_KEYCODE(DPAD_RIGHT), \ - DEFINE_KEYCODE(DPAD_CENTER), \ - DEFINE_KEYCODE(VOLUME_UP), \ - DEFINE_KEYCODE(VOLUME_DOWN), \ - DEFINE_KEYCODE(POWER), \ - DEFINE_KEYCODE(CAMERA), \ - DEFINE_KEYCODE(CLEAR), \ - DEFINE_KEYCODE(A), \ - DEFINE_KEYCODE(B), \ - DEFINE_KEYCODE(C), \ - DEFINE_KEYCODE(D), \ - DEFINE_KEYCODE(E), \ - DEFINE_KEYCODE(F), \ - DEFINE_KEYCODE(G), \ - DEFINE_KEYCODE(H), \ - DEFINE_KEYCODE(I), \ - DEFINE_KEYCODE(J), \ - DEFINE_KEYCODE(K), \ - DEFINE_KEYCODE(L), \ - DEFINE_KEYCODE(M), \ - DEFINE_KEYCODE(N), \ - DEFINE_KEYCODE(O), \ - DEFINE_KEYCODE(P), \ - DEFINE_KEYCODE(Q), \ - DEFINE_KEYCODE(R), \ - DEFINE_KEYCODE(S), \ - DEFINE_KEYCODE(T), \ - DEFINE_KEYCODE(U), \ - DEFINE_KEYCODE(V), \ - DEFINE_KEYCODE(W), \ - DEFINE_KEYCODE(X), \ - DEFINE_KEYCODE(Y), \ - DEFINE_KEYCODE(Z), \ - DEFINE_KEYCODE(COMMA), \ - DEFINE_KEYCODE(PERIOD), \ - DEFINE_KEYCODE(ALT_LEFT), \ - DEFINE_KEYCODE(ALT_RIGHT), \ - DEFINE_KEYCODE(SHIFT_LEFT), \ - DEFINE_KEYCODE(SHIFT_RIGHT), \ - DEFINE_KEYCODE(TAB), \ - DEFINE_KEYCODE(SPACE), \ - DEFINE_KEYCODE(SYM), \ - DEFINE_KEYCODE(EXPLORER), \ - DEFINE_KEYCODE(ENVELOPE), \ - DEFINE_KEYCODE(ENTER), \ - DEFINE_KEYCODE(DEL), \ - DEFINE_KEYCODE(GRAVE), \ - DEFINE_KEYCODE(MINUS), \ - DEFINE_KEYCODE(EQUALS), \ - DEFINE_KEYCODE(LEFT_BRACKET), \ - DEFINE_KEYCODE(RIGHT_BRACKET), \ - DEFINE_KEYCODE(BACKSLASH), \ - DEFINE_KEYCODE(SEMICOLON), \ - DEFINE_KEYCODE(APOSTROPHE), \ - DEFINE_KEYCODE(SLASH), \ - DEFINE_KEYCODE(AT), \ - DEFINE_KEYCODE(NUM), \ - DEFINE_KEYCODE(HEADSETHOOK), \ - DEFINE_KEYCODE(FOCUS), \ - DEFINE_KEYCODE(PLUS), \ - DEFINE_KEYCODE(MENU), \ - DEFINE_KEYCODE(NOTIFICATION), \ - DEFINE_KEYCODE(SEARCH), \ - DEFINE_KEYCODE(MEDIA_PLAY_PAUSE), \ - DEFINE_KEYCODE(MEDIA_STOP), \ - DEFINE_KEYCODE(MEDIA_NEXT), \ - DEFINE_KEYCODE(MEDIA_PREVIOUS), \ - DEFINE_KEYCODE(MEDIA_REWIND), \ - DEFINE_KEYCODE(MEDIA_FAST_FORWARD), \ - DEFINE_KEYCODE(MUTE), \ - DEFINE_KEYCODE(PAGE_UP), \ - DEFINE_KEYCODE(PAGE_DOWN), \ - DEFINE_KEYCODE(PICTSYMBOLS), \ - DEFINE_KEYCODE(SWITCH_CHARSET), \ - DEFINE_KEYCODE(BUTTON_A), \ - DEFINE_KEYCODE(BUTTON_B), \ - DEFINE_KEYCODE(BUTTON_C), \ - DEFINE_KEYCODE(BUTTON_X), \ - DEFINE_KEYCODE(BUTTON_Y), \ - DEFINE_KEYCODE(BUTTON_Z), \ - DEFINE_KEYCODE(BUTTON_L1), \ - DEFINE_KEYCODE(BUTTON_R1), \ - DEFINE_KEYCODE(BUTTON_L2), \ - DEFINE_KEYCODE(BUTTON_R2), \ - DEFINE_KEYCODE(BUTTON_THUMBL), \ - DEFINE_KEYCODE(BUTTON_THUMBR), \ - DEFINE_KEYCODE(BUTTON_START), \ - DEFINE_KEYCODE(BUTTON_SELECT), \ - DEFINE_KEYCODE(BUTTON_MODE), \ - DEFINE_KEYCODE(ESCAPE), \ - DEFINE_KEYCODE(FORWARD_DEL), \ - DEFINE_KEYCODE(CTRL_LEFT), \ - DEFINE_KEYCODE(CTRL_RIGHT), \ - DEFINE_KEYCODE(CAPS_LOCK), \ - DEFINE_KEYCODE(SCROLL_LOCK), \ - DEFINE_KEYCODE(META_LEFT), \ - DEFINE_KEYCODE(META_RIGHT), \ - DEFINE_KEYCODE(FUNCTION), \ - DEFINE_KEYCODE(SYSRQ), \ - DEFINE_KEYCODE(BREAK), \ - DEFINE_KEYCODE(MOVE_HOME), \ - DEFINE_KEYCODE(MOVE_END), \ - DEFINE_KEYCODE(INSERT), \ - DEFINE_KEYCODE(FORWARD), \ - DEFINE_KEYCODE(MEDIA_PLAY), \ - DEFINE_KEYCODE(MEDIA_PAUSE), \ - DEFINE_KEYCODE(MEDIA_CLOSE), \ - DEFINE_KEYCODE(MEDIA_EJECT), \ - DEFINE_KEYCODE(MEDIA_RECORD), \ - DEFINE_KEYCODE(F1), \ - DEFINE_KEYCODE(F2), \ - DEFINE_KEYCODE(F3), \ - DEFINE_KEYCODE(F4), \ - DEFINE_KEYCODE(F5), \ - DEFINE_KEYCODE(F6), \ - DEFINE_KEYCODE(F7), \ - DEFINE_KEYCODE(F8), \ - DEFINE_KEYCODE(F9), \ - DEFINE_KEYCODE(F10), \ - DEFINE_KEYCODE(F11), \ - DEFINE_KEYCODE(F12), \ - DEFINE_KEYCODE(NUM_LOCK), \ - DEFINE_KEYCODE(NUMPAD_0), \ - DEFINE_KEYCODE(NUMPAD_1), \ - DEFINE_KEYCODE(NUMPAD_2), \ - DEFINE_KEYCODE(NUMPAD_3), \ - DEFINE_KEYCODE(NUMPAD_4), \ - DEFINE_KEYCODE(NUMPAD_5), \ - DEFINE_KEYCODE(NUMPAD_6), \ - DEFINE_KEYCODE(NUMPAD_7), \ - DEFINE_KEYCODE(NUMPAD_8), \ - DEFINE_KEYCODE(NUMPAD_9), \ - DEFINE_KEYCODE(NUMPAD_DIVIDE), \ - DEFINE_KEYCODE(NUMPAD_MULTIPLY), \ - DEFINE_KEYCODE(NUMPAD_SUBTRACT), \ - DEFINE_KEYCODE(NUMPAD_ADD), \ - DEFINE_KEYCODE(NUMPAD_DOT), \ - DEFINE_KEYCODE(NUMPAD_COMMA), \ - DEFINE_KEYCODE(NUMPAD_ENTER), \ - DEFINE_KEYCODE(NUMPAD_EQUALS), \ - DEFINE_KEYCODE(NUMPAD_LEFT_PAREN), \ - DEFINE_KEYCODE(NUMPAD_RIGHT_PAREN), \ - DEFINE_KEYCODE(VOLUME_MUTE), \ - DEFINE_KEYCODE(INFO), \ - DEFINE_KEYCODE(CHANNEL_UP), \ - DEFINE_KEYCODE(CHANNEL_DOWN), \ - DEFINE_KEYCODE(ZOOM_IN), \ - DEFINE_KEYCODE(ZOOM_OUT), \ - DEFINE_KEYCODE(TV), \ - DEFINE_KEYCODE(WINDOW), \ - DEFINE_KEYCODE(GUIDE), \ - DEFINE_KEYCODE(DVR), \ - DEFINE_KEYCODE(BOOKMARK), \ - DEFINE_KEYCODE(CAPTIONS), \ - DEFINE_KEYCODE(SETTINGS), \ - DEFINE_KEYCODE(TV_POWER), \ - DEFINE_KEYCODE(TV_INPUT), \ - DEFINE_KEYCODE(STB_POWER), \ - DEFINE_KEYCODE(STB_INPUT), \ - DEFINE_KEYCODE(AVR_POWER), \ - DEFINE_KEYCODE(AVR_INPUT), \ - DEFINE_KEYCODE(PROG_RED), \ - DEFINE_KEYCODE(PROG_GREEN), \ - DEFINE_KEYCODE(PROG_YELLOW), \ - DEFINE_KEYCODE(PROG_BLUE), \ - DEFINE_KEYCODE(APP_SWITCH), \ - DEFINE_KEYCODE(BUTTON_1), \ - DEFINE_KEYCODE(BUTTON_2), \ - DEFINE_KEYCODE(BUTTON_3), \ - DEFINE_KEYCODE(BUTTON_4), \ - DEFINE_KEYCODE(BUTTON_5), \ - DEFINE_KEYCODE(BUTTON_6), \ - DEFINE_KEYCODE(BUTTON_7), \ - DEFINE_KEYCODE(BUTTON_8), \ - DEFINE_KEYCODE(BUTTON_9), \ - DEFINE_KEYCODE(BUTTON_10), \ - DEFINE_KEYCODE(BUTTON_11), \ - DEFINE_KEYCODE(BUTTON_12), \ - DEFINE_KEYCODE(BUTTON_13), \ - DEFINE_KEYCODE(BUTTON_14), \ - DEFINE_KEYCODE(BUTTON_15), \ - DEFINE_KEYCODE(BUTTON_16), \ - DEFINE_KEYCODE(LANGUAGE_SWITCH), \ - DEFINE_KEYCODE(MANNER_MODE), \ - DEFINE_KEYCODE(3D_MODE), \ - DEFINE_KEYCODE(CONTACTS), \ - DEFINE_KEYCODE(CALENDAR), \ - DEFINE_KEYCODE(MUSIC), \ - DEFINE_KEYCODE(CALCULATOR), \ - DEFINE_KEYCODE(ZENKAKU_HANKAKU), \ - DEFINE_KEYCODE(EISU), \ - DEFINE_KEYCODE(MUHENKAN), \ - DEFINE_KEYCODE(HENKAN), \ - DEFINE_KEYCODE(KATAKANA_HIRAGANA), \ - DEFINE_KEYCODE(YEN), \ - DEFINE_KEYCODE(RO), \ - DEFINE_KEYCODE(KANA), \ - DEFINE_KEYCODE(ASSIST), \ - DEFINE_KEYCODE(BRIGHTNESS_DOWN), \ - DEFINE_KEYCODE(BRIGHTNESS_UP), \ - DEFINE_KEYCODE(MEDIA_AUDIO_TRACK), \ - DEFINE_KEYCODE(SLEEP), \ - DEFINE_KEYCODE(WAKEUP), \ - DEFINE_KEYCODE(PAIRING), \ - DEFINE_KEYCODE(MEDIA_TOP_MENU), \ - DEFINE_KEYCODE(11), \ - DEFINE_KEYCODE(12), \ - DEFINE_KEYCODE(LAST_CHANNEL), \ - DEFINE_KEYCODE(TV_DATA_SERVICE), \ - DEFINE_KEYCODE(VOICE_ASSIST), \ - DEFINE_KEYCODE(TV_RADIO_SERVICE), \ - DEFINE_KEYCODE(TV_TELETEXT), \ - DEFINE_KEYCODE(TV_NUMBER_ENTRY), \ - DEFINE_KEYCODE(TV_TERRESTRIAL_ANALOG), \ - DEFINE_KEYCODE(TV_TERRESTRIAL_DIGITAL), \ - DEFINE_KEYCODE(TV_SATELLITE), \ - DEFINE_KEYCODE(TV_SATELLITE_BS), \ - DEFINE_KEYCODE(TV_SATELLITE_CS), \ - DEFINE_KEYCODE(TV_SATELLITE_SERVICE), \ - DEFINE_KEYCODE(TV_NETWORK), \ - DEFINE_KEYCODE(TV_ANTENNA_CABLE), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_1), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_2), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_3), \ - DEFINE_KEYCODE(TV_INPUT_HDMI_4), \ - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_1), \ - DEFINE_KEYCODE(TV_INPUT_COMPOSITE_2), \ - DEFINE_KEYCODE(TV_INPUT_COMPONENT_1), \ - DEFINE_KEYCODE(TV_INPUT_COMPONENT_2), \ - DEFINE_KEYCODE(TV_INPUT_VGA_1), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_UP), \ - DEFINE_KEYCODE(TV_AUDIO_DESCRIPTION_MIX_DOWN), \ - DEFINE_KEYCODE(TV_ZOOM_MODE), \ - DEFINE_KEYCODE(TV_CONTENTS_MENU), \ - DEFINE_KEYCODE(TV_MEDIA_CONTEXT_MENU), \ - DEFINE_KEYCODE(TV_TIMER_PROGRAMMING), \ - DEFINE_KEYCODE(HELP), \ - DEFINE_KEYCODE(NAVIGATE_PREVIOUS), \ - DEFINE_KEYCODE(NAVIGATE_NEXT), \ - DEFINE_KEYCODE(NAVIGATE_IN), \ - DEFINE_KEYCODE(NAVIGATE_OUT), \ - DEFINE_KEYCODE(STEM_PRIMARY), \ - DEFINE_KEYCODE(STEM_1), \ - DEFINE_KEYCODE(STEM_2), \ - DEFINE_KEYCODE(STEM_3), \ - DEFINE_KEYCODE(DPAD_UP_LEFT), \ - DEFINE_KEYCODE(DPAD_DOWN_LEFT), \ - DEFINE_KEYCODE(DPAD_UP_RIGHT), \ - DEFINE_KEYCODE(DPAD_DOWN_RIGHT), \ - DEFINE_KEYCODE(MEDIA_SKIP_FORWARD), \ - DEFINE_KEYCODE(MEDIA_SKIP_BACKWARD), \ - DEFINE_KEYCODE(MEDIA_STEP_FORWARD), \ - DEFINE_KEYCODE(MEDIA_STEP_BACKWARD), \ - DEFINE_KEYCODE(SOFT_SLEEP), \ - DEFINE_KEYCODE(CUT), \ - DEFINE_KEYCODE(COPY), \ - DEFINE_KEYCODE(PASTE), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_UP), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_DOWN), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_LEFT), \ - DEFINE_KEYCODE(SYSTEM_NAVIGATION_RIGHT), \ - DEFINE_KEYCODE(ALL_APPS), \ - DEFINE_KEYCODE(REFRESH), \ - DEFINE_KEYCODE(THUMBS_UP), \ - DEFINE_KEYCODE(THUMBS_DOWN), \ - DEFINE_KEYCODE(PROFILE_SWITCH), \ - DEFINE_KEYCODE(VIDEO_APP_1), \ - DEFINE_KEYCODE(VIDEO_APP_2), \ - DEFINE_KEYCODE(VIDEO_APP_3), \ - DEFINE_KEYCODE(VIDEO_APP_4), \ - DEFINE_KEYCODE(VIDEO_APP_5), \ - DEFINE_KEYCODE(VIDEO_APP_6), \ - DEFINE_KEYCODE(VIDEO_APP_7), \ - DEFINE_KEYCODE(VIDEO_APP_8), \ - DEFINE_KEYCODE(FEATURED_APP_1), \ - DEFINE_KEYCODE(FEATURED_APP_2), \ - DEFINE_KEYCODE(FEATURED_APP_3), \ - DEFINE_KEYCODE(FEATURED_APP_4), \ - DEFINE_KEYCODE(DEMO_APP_1), \ - DEFINE_KEYCODE(DEMO_APP_2), \ - DEFINE_KEYCODE(DEMO_APP_3), \ - DEFINE_KEYCODE(DEMO_APP_4), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_DOWN), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_UP), \ - DEFINE_KEYCODE(KEYBOARD_BACKLIGHT_TOGGLE), \ - DEFINE_KEYCODE(STYLUS_BUTTON_PRIMARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_SECONDARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_TERTIARY), \ - DEFINE_KEYCODE(STYLUS_BUTTON_TAIL), \ - DEFINE_KEYCODE(RECENT_APPS), \ - DEFINE_KEYCODE(MACRO_1), \ - DEFINE_KEYCODE(MACRO_2), \ - DEFINE_KEYCODE(MACRO_3), \ - DEFINE_KEYCODE(MACRO_4) -// DEFINE_KEYCODE(EMOJI_PICKER), \ -// DEFINE_KEYCODE(SCREENSHOT), \ -// DEFINE_KEYCODE(DICTATE), \ -// DEFINE_KEYCODE(NEW), \ -// DEFINE_KEYCODE(CLOSE), \ -// DEFINE_KEYCODE(DO_NOT_DISTURB), \ -// DEFINE_KEYCODE(PRINT), \ -// DEFINE_KEYCODE(LOCK), \ -// DEFINE_KEYCODE(FULLSCREEN), \ -// DEFINE_KEYCODE(F13), \ -// DEFINE_KEYCODE(F14), \ -// DEFINE_KEYCODE(F15), \ -// DEFINE_KEYCODE(F16), \ -// DEFINE_KEYCODE(F17), \ -// DEFINE_KEYCODE(F18), \ -// DEFINE_KEYCODE(F19),\ -// DEFINE_KEYCODE(F20), \ -// DEFINE_KEYCODE(F21), \ -// DEFINE_KEYCODE(F22), \ -// DEFINE_KEYCODE(F23), \ -// DEFINE_KEYCODE(F24) - -// NOTE: If you add a new axis here you must also add it to several other files. -// Refer to frameworks/base/core/java/android/view/MotionEvent.java for the full list. -#define AXES_SEQUENCE \ - DEFINE_AXIS(X), \ - DEFINE_AXIS(Y), \ - DEFINE_AXIS(PRESSURE), \ - DEFINE_AXIS(SIZE), \ - DEFINE_AXIS(TOUCH_MAJOR), \ - DEFINE_AXIS(TOUCH_MINOR), \ - DEFINE_AXIS(TOOL_MAJOR), \ - DEFINE_AXIS(TOOL_MINOR), \ - DEFINE_AXIS(ORIENTATION), \ - DEFINE_AXIS(VSCROLL), \ - DEFINE_AXIS(HSCROLL), \ - DEFINE_AXIS(Z), \ - DEFINE_AXIS(RX), \ - DEFINE_AXIS(RY), \ - DEFINE_AXIS(RZ), \ - DEFINE_AXIS(HAT_X), \ - DEFINE_AXIS(HAT_Y), \ - DEFINE_AXIS(LTRIGGER), \ - DEFINE_AXIS(RTRIGGER), \ - DEFINE_AXIS(THROTTLE), \ - DEFINE_AXIS(RUDDER), \ - DEFINE_AXIS(WHEEL), \ - DEFINE_AXIS(GAS), \ - DEFINE_AXIS(BRAKE), \ - DEFINE_AXIS(DISTANCE), \ - DEFINE_AXIS(TILT), \ - DEFINE_AXIS(SCROLL), \ - DEFINE_AXIS(RELATIVE_X), \ - DEFINE_AXIS(RELATIVE_Y), \ - {"RESERVED_29", 29}, \ - {"RESERVED_30", 30}, \ - {"RESERVED_31", 31}, \ - DEFINE_AXIS(GENERIC_1), \ - DEFINE_AXIS(GENERIC_2), \ - DEFINE_AXIS(GENERIC_3), \ - DEFINE_AXIS(GENERIC_4), \ - DEFINE_AXIS(GENERIC_5), \ - DEFINE_AXIS(GENERIC_6), \ - DEFINE_AXIS(GENERIC_7), \ - DEFINE_AXIS(GENERIC_8), \ - DEFINE_AXIS(GENERIC_9), \ - DEFINE_AXIS(GENERIC_10), \ - DEFINE_AXIS(GENERIC_11), \ - DEFINE_AXIS(GENERIC_12), \ - DEFINE_AXIS(GENERIC_13), \ - DEFINE_AXIS(GENERIC_14), \ - DEFINE_AXIS(GENERIC_15), \ - DEFINE_AXIS(GENERIC_16), \ - DEFINE_AXIS(GESTURE_X_OFFSET), \ - DEFINE_AXIS(GESTURE_Y_OFFSET), \ - DEFINE_AXIS(GESTURE_SCROLL_X_DISTANCE), \ - DEFINE_AXIS(GESTURE_SCROLL_Y_DISTANCE), \ - DEFINE_AXIS(GESTURE_PINCH_SCALE_FACTOR), \ - DEFINE_AXIS(GESTURE_SWIPE_FINGER_COUNT) - -#define FLAGS_SEQUENCE \ - DEFINE_FLAG(VIRTUAL), \ - DEFINE_FLAG(FUNCTION), \ - DEFINE_FLAG(GESTURE), \ - DEFINE_FLAG(WAKE), \ - DEFINE_FLAG(FALLBACK_USAGE_MAPPING) - -// clang-format on - -// --- InputEventLookup --- - - InputEventLookup::InputEventLookup() - : KEYCODES({KEYCODES_SEQUENCE}), - KEY_NAMES({KEYCODES_SEQUENCE}), - AXES({AXES_SEQUENCE}), - AXES_NAMES({AXES_SEQUENCE}), - FLAGS({FLAGS_SEQUENCE}) {} - - std::optional InputEventLookup::lookupValueByLabel( - const std::unordered_map &map, const char *literal) { - std::string str(literal); - auto it = map.find(str); - return it != map.end() ? std::make_optional(it->second) : std::nullopt; - } - - const char *InputEventLookup::lookupLabelByValue(const std::vector &vec, - int value) { - if (static_cast(value) < vec.size()) { - return vec[value].literal; - } - return nullptr; - } - - std::optional InputEventLookup::getKeyCodeByLabel(const char *label) { - const auto &self = get(); - return self.lookupValueByLabel(self.KEYCODES, label); - } - - const char *InputEventLookup::getLabelByKeyCode(int32_t keyCode) { - const auto &self = get(); - if (keyCode >= 0 && static_cast(keyCode) < self.KEYCODES.size()) { - return get().lookupLabelByValue(self.KEY_NAMES, keyCode); - } - return nullptr; - } - - std::optional InputEventLookup::getKeyFlagByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.FLAGS, label); - } - - std::optional InputEventLookup::getAxisByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.AXES, label); - } - - const char *InputEventLookup::getAxisLabel(int32_t axisId) { - const auto &self = get(); - return lookupLabelByValue(self.AXES_NAMES, axisId); - } - - std::optional InputEventLookup::getLedByLabel(const char *label) { - const auto &self = get(); - return lookupValueByLabel(self.LEDS, label); - } - - namespace { - - struct label { - const char *name; - int value; - }; - -#define LABEL(constant) \ - { #constant, constant } -#define LABEL_END \ - { nullptr, -1 } - -// Inserted from the file: out/soong/.intermediates/system/core/toolbox/toolbox_input_labels/gen/input.h-labels.h - static struct label ev_key_value_labels[] = { - {"UP", 0}, - {"DOWN", 1}, - {"REPEAT", 2}, - LABEL_END, - }; - - - static struct label input_prop_labels[] = { - LABEL(INPUT_PROP_POINTER), - LABEL(INPUT_PROP_DIRECT), - LABEL(INPUT_PROP_BUTTONPAD), - LABEL(INPUT_PROP_SEMI_MT), - LABEL(INPUT_PROP_TOPBUTTONPAD), - LABEL(INPUT_PROP_POINTING_STICK), - LABEL(INPUT_PROP_ACCELEROMETER), - LABEL(INPUT_PROP_MAX), - LABEL_END, - }; - static struct label ev_labels[] = { - LABEL(EV_VERSION), - LABEL(EV_SYN), - LABEL(EV_KEY), - LABEL(EV_REL), - LABEL(EV_ABS), - LABEL(EV_MSC), - LABEL(EV_SW), - LABEL(EV_LED), - LABEL(EV_SND), - LABEL(EV_REP), - LABEL(EV_FF), - LABEL(EV_PWR), - LABEL(EV_FF_STATUS), - LABEL(EV_MAX), - LABEL_END, - }; - static struct label syn_labels[] = { - LABEL(SYN_REPORT), - LABEL(SYN_CONFIG), - LABEL(SYN_MT_REPORT), - LABEL(SYN_DROPPED), - LABEL(SYN_MAX), - LABEL_END, - }; - static struct label key_labels[] = { - LABEL(KEY_RESERVED), - LABEL(KEY_ESC), - LABEL(KEY_1), - LABEL(KEY_2), - LABEL(KEY_3), - LABEL(KEY_4), - LABEL(KEY_5), - LABEL(KEY_6), - LABEL(KEY_7), - LABEL(KEY_8), - LABEL(KEY_9), - LABEL(KEY_0), - LABEL(KEY_MINUS), - LABEL(KEY_EQUAL), - LABEL(KEY_BACKSPACE), - LABEL(KEY_TAB), - LABEL(KEY_Q), - LABEL(KEY_W), - LABEL(KEY_E), - LABEL(KEY_R), - LABEL(KEY_T), - LABEL(KEY_Y), - LABEL(KEY_U), - LABEL(KEY_I), - LABEL(KEY_O), - LABEL(KEY_P), - LABEL(KEY_LEFTBRACE), - LABEL(KEY_RIGHTBRACE), - LABEL(KEY_ENTER), - LABEL(KEY_LEFTCTRL), - LABEL(KEY_A), - LABEL(KEY_S), - LABEL(KEY_D), - LABEL(KEY_F), - LABEL(KEY_G), - LABEL(KEY_H), - LABEL(KEY_J), - LABEL(KEY_K), - LABEL(KEY_L), - LABEL(KEY_SEMICOLON), - LABEL(KEY_APOSTROPHE), - LABEL(KEY_GRAVE), - LABEL(KEY_LEFTSHIFT), - LABEL(KEY_BACKSLASH), - LABEL(KEY_Z), - LABEL(KEY_X), - LABEL(KEY_C), - LABEL(KEY_V), - LABEL(KEY_B), - LABEL(KEY_N), - LABEL(KEY_M), - LABEL(KEY_COMMA), - LABEL(KEY_DOT), - LABEL(KEY_SLASH), - LABEL(KEY_RIGHTSHIFT), - LABEL(KEY_KPASTERISK), - LABEL(KEY_LEFTALT), - LABEL(KEY_SPACE), - LABEL(KEY_CAPSLOCK), - LABEL(KEY_F1), - LABEL(KEY_F2), - LABEL(KEY_F3), - LABEL(KEY_F4), - LABEL(KEY_F5), - LABEL(KEY_F6), - LABEL(KEY_F7), - LABEL(KEY_F8), - LABEL(KEY_F9), - LABEL(KEY_F10), - LABEL(KEY_NUMLOCK), - LABEL(KEY_SCROLLLOCK), - LABEL(KEY_KP7), - LABEL(KEY_KP8), - LABEL(KEY_KP9), - LABEL(KEY_KPMINUS), - LABEL(KEY_KP4), - LABEL(KEY_KP5), - LABEL(KEY_KP6), - LABEL(KEY_KPPLUS), - LABEL(KEY_KP1), - LABEL(KEY_KP2), - LABEL(KEY_KP3), - LABEL(KEY_KP0), - LABEL(KEY_KPDOT), - LABEL(KEY_ZENKAKUHANKAKU), - LABEL(KEY_102ND), - LABEL(KEY_F11), - LABEL(KEY_F12), - LABEL(KEY_RO), - LABEL(KEY_KATAKANA), - LABEL(KEY_HIRAGANA), - LABEL(KEY_HENKAN), - LABEL(KEY_KATAKANAHIRAGANA), - LABEL(KEY_MUHENKAN), - LABEL(KEY_KPJPCOMMA), - LABEL(KEY_KPENTER), - LABEL(KEY_RIGHTCTRL), - LABEL(KEY_KPSLASH), - LABEL(KEY_SYSRQ), - LABEL(KEY_RIGHTALT), - LABEL(KEY_LINEFEED), - LABEL(KEY_HOME), - LABEL(KEY_UP), - LABEL(KEY_PAGEUP), - LABEL(KEY_LEFT), - LABEL(KEY_RIGHT), - LABEL(KEY_END), - LABEL(KEY_DOWN), - LABEL(KEY_PAGEDOWN), - LABEL(KEY_INSERT), - LABEL(KEY_DELETE), - LABEL(KEY_MACRO), - LABEL(KEY_MUTE), - LABEL(KEY_VOLUMEDOWN), - LABEL(KEY_VOLUMEUP), - LABEL(KEY_POWER), - LABEL(KEY_KPEQUAL), - LABEL(KEY_KPPLUSMINUS), - LABEL(KEY_PAUSE), - LABEL(KEY_SCALE), - LABEL(KEY_KPCOMMA), - LABEL(KEY_HANGEUL), - LABEL(KEY_HANJA), - LABEL(KEY_YEN), - LABEL(KEY_LEFTMETA), - LABEL(KEY_RIGHTMETA), - LABEL(KEY_COMPOSE), - LABEL(KEY_STOP), - LABEL(KEY_AGAIN), - LABEL(KEY_PROPS), - LABEL(KEY_UNDO), - LABEL(KEY_FRONT), - LABEL(KEY_COPY), - LABEL(KEY_OPEN), - LABEL(KEY_PASTE), - LABEL(KEY_FIND), - LABEL(KEY_CUT), - LABEL(KEY_HELP), - LABEL(KEY_MENU), - LABEL(KEY_CALC), - LABEL(KEY_SETUP), - LABEL(KEY_SLEEP), - LABEL(KEY_WAKEUP), - LABEL(KEY_FILE), - LABEL(KEY_SENDFILE), - LABEL(KEY_DELETEFILE), - LABEL(KEY_XFER), - LABEL(KEY_PROG1), - LABEL(KEY_PROG2), - LABEL(KEY_WWW), - LABEL(KEY_MSDOS), - LABEL(KEY_COFFEE), - LABEL(KEY_ROTATE_DISPLAY), - LABEL(KEY_CYCLEWINDOWS), - LABEL(KEY_MAIL), - LABEL(KEY_BOOKMARKS), - LABEL(KEY_COMPUTER), - LABEL(KEY_BACK), - LABEL(KEY_FORWARD), - LABEL(KEY_CLOSECD), - LABEL(KEY_EJECTCD), - LABEL(KEY_EJECTCLOSECD), - LABEL(KEY_NEXTSONG), - LABEL(KEY_PLAYPAUSE), - LABEL(KEY_PREVIOUSSONG), - LABEL(KEY_STOPCD), - LABEL(KEY_RECORD), - LABEL(KEY_REWIND), - LABEL(KEY_PHONE), - LABEL(KEY_ISO), - LABEL(KEY_CONFIG), - LABEL(KEY_HOMEPAGE), - LABEL(KEY_REFRESH), - LABEL(KEY_EXIT), - LABEL(KEY_MOVE), - LABEL(KEY_EDIT), - LABEL(KEY_SCROLLUP), - LABEL(KEY_SCROLLDOWN), - LABEL(KEY_KPLEFTPAREN), - LABEL(KEY_KPRIGHTPAREN), - LABEL(KEY_NEW), - LABEL(KEY_REDO), - LABEL(KEY_F13), - LABEL(KEY_F14), - LABEL(KEY_F15), - LABEL(KEY_F16), - LABEL(KEY_F17), - LABEL(KEY_F18), - LABEL(KEY_F19), - LABEL(KEY_F20), - LABEL(KEY_F21), - LABEL(KEY_F22), - LABEL(KEY_F23), - LABEL(KEY_F24), - LABEL(KEY_PLAYCD), - LABEL(KEY_PAUSECD), - LABEL(KEY_PROG3), - LABEL(KEY_PROG4), - LABEL(KEY_ALL_APPLICATIONS), - LABEL(KEY_SUSPEND), - LABEL(KEY_CLOSE), - LABEL(KEY_PLAY), - LABEL(KEY_FASTFORWARD), - LABEL(KEY_BASSBOOST), - LABEL(KEY_PRINT), - LABEL(KEY_HP), - LABEL(KEY_CAMERA), - LABEL(KEY_SOUND), - LABEL(KEY_QUESTION), - LABEL(KEY_EMAIL), - LABEL(KEY_CHAT), - LABEL(KEY_SEARCH), - LABEL(KEY_CONNECT), - LABEL(KEY_FINANCE), - LABEL(KEY_SPORT), - LABEL(KEY_SHOP), - LABEL(KEY_ALTERASE), - LABEL(KEY_CANCEL), - LABEL(KEY_BRIGHTNESSDOWN), - LABEL(KEY_BRIGHTNESSUP), - LABEL(KEY_MEDIA), - LABEL(KEY_SWITCHVIDEOMODE), - LABEL(KEY_KBDILLUMTOGGLE), - LABEL(KEY_KBDILLUMDOWN), - LABEL(KEY_KBDILLUMUP), - LABEL(KEY_SEND), - LABEL(KEY_REPLY), - LABEL(KEY_FORWARDMAIL), - LABEL(KEY_SAVE), - LABEL(KEY_DOCUMENTS), - LABEL(KEY_BATTERY), - LABEL(KEY_BLUETOOTH), - LABEL(KEY_WLAN), - LABEL(KEY_UWB), - LABEL(KEY_UNKNOWN), - LABEL(KEY_VIDEO_NEXT), - LABEL(KEY_VIDEO_PREV), - LABEL(KEY_BRIGHTNESS_CYCLE), - LABEL(KEY_BRIGHTNESS_AUTO), - LABEL(KEY_DISPLAY_OFF), - LABEL(KEY_WWAN), - LABEL(KEY_RFKILL), - LABEL(KEY_MICMUTE), - LABEL(BTN_MISC), - LABEL(BTN_0), - LABEL(BTN_1), - LABEL(BTN_2), - LABEL(BTN_3), - LABEL(BTN_4), - LABEL(BTN_5), - LABEL(BTN_6), - LABEL(BTN_7), - LABEL(BTN_8), - LABEL(BTN_9), - LABEL(BTN_MOUSE), - LABEL(BTN_LEFT), - LABEL(BTN_RIGHT), - LABEL(BTN_MIDDLE), - LABEL(BTN_SIDE), - LABEL(BTN_EXTRA), - LABEL(BTN_FORWARD), - LABEL(BTN_BACK), - LABEL(BTN_TASK), - LABEL(BTN_JOYSTICK), - LABEL(BTN_TRIGGER), - LABEL(BTN_THUMB), - LABEL(BTN_THUMB2), - LABEL(BTN_TOP), - LABEL(BTN_TOP2), - LABEL(BTN_PINKIE), - LABEL(BTN_BASE), - LABEL(BTN_BASE2), - LABEL(BTN_BASE3), - LABEL(BTN_BASE4), - LABEL(BTN_BASE5), - LABEL(BTN_BASE6), - LABEL(BTN_DEAD), - LABEL(BTN_GAMEPAD), - LABEL(BTN_SOUTH), - LABEL(BTN_EAST), - LABEL(BTN_C), - LABEL(BTN_NORTH), - LABEL(BTN_WEST), - LABEL(BTN_Z), - LABEL(BTN_TL), - LABEL(BTN_TR), - LABEL(BTN_TL2), - LABEL(BTN_TR2), - LABEL(BTN_SELECT), - LABEL(BTN_START), - LABEL(BTN_MODE), - LABEL(BTN_THUMBL), - LABEL(BTN_THUMBR), - LABEL(BTN_DIGI), - LABEL(BTN_TOOL_PEN), - LABEL(BTN_TOOL_RUBBER), - LABEL(BTN_TOOL_BRUSH), - LABEL(BTN_TOOL_PENCIL), - LABEL(BTN_TOOL_AIRBRUSH), - LABEL(BTN_TOOL_FINGER), - LABEL(BTN_TOOL_MOUSE), - LABEL(BTN_TOOL_LENS), - LABEL(BTN_TOOL_QUINTTAP), - LABEL(BTN_STYLUS3), - LABEL(BTN_TOUCH), - LABEL(BTN_STYLUS), - LABEL(BTN_STYLUS2), - LABEL(BTN_TOOL_DOUBLETAP), - LABEL(BTN_TOOL_TRIPLETAP), - LABEL(BTN_TOOL_QUADTAP), - LABEL(BTN_WHEEL), - LABEL(BTN_GEAR_DOWN), - LABEL(BTN_GEAR_UP), - LABEL(KEY_OK), - LABEL(KEY_SELECT), - LABEL(KEY_GOTO), - LABEL(KEY_CLEAR), - LABEL(KEY_POWER2), - LABEL(KEY_OPTION), - LABEL(KEY_INFO), - LABEL(KEY_TIME), - LABEL(KEY_VENDOR), - LABEL(KEY_ARCHIVE), - LABEL(KEY_PROGRAM), - LABEL(KEY_CHANNEL), - LABEL(KEY_FAVORITES), - LABEL(KEY_EPG), - LABEL(KEY_PVR), - LABEL(KEY_MHP), - LABEL(KEY_LANGUAGE), - LABEL(KEY_TITLE), - LABEL(KEY_SUBTITLE), - LABEL(KEY_ANGLE), - LABEL(KEY_FULL_SCREEN), - LABEL(KEY_MODE), - LABEL(KEY_KEYBOARD), - LABEL(KEY_ASPECT_RATIO), - LABEL(KEY_PC), - LABEL(KEY_TV), - LABEL(KEY_TV2), - LABEL(KEY_VCR), - LABEL(KEY_VCR2), - LABEL(KEY_SAT), - LABEL(KEY_SAT2), - LABEL(KEY_CD), - LABEL(KEY_TAPE), - LABEL(KEY_RADIO), - LABEL(KEY_TUNER), - LABEL(KEY_PLAYER), - LABEL(KEY_TEXT), - LABEL(KEY_DVD), - LABEL(KEY_AUX), - LABEL(KEY_MP3), - LABEL(KEY_AUDIO), - LABEL(KEY_VIDEO), - LABEL(KEY_DIRECTORY), - LABEL(KEY_LIST), - LABEL(KEY_MEMO), - LABEL(KEY_CALENDAR), - LABEL(KEY_RED), - LABEL(KEY_GREEN), - LABEL(KEY_YELLOW), - LABEL(KEY_BLUE), - LABEL(KEY_CHANNELUP), - LABEL(KEY_CHANNELDOWN), - LABEL(KEY_FIRST), - LABEL(KEY_LAST), - LABEL(KEY_AB), - LABEL(KEY_NEXT), - LABEL(KEY_RESTART), - LABEL(KEY_SLOW), - LABEL(KEY_SHUFFLE), - LABEL(KEY_BREAK), - LABEL(KEY_PREVIOUS), - LABEL(KEY_DIGITS), - LABEL(KEY_TEEN), - LABEL(KEY_TWEN), - LABEL(KEY_VIDEOPHONE), - LABEL(KEY_GAMES), - LABEL(KEY_ZOOMIN), - LABEL(KEY_ZOOMOUT), - LABEL(KEY_ZOOMRESET), - LABEL(KEY_WORDPROCESSOR), - LABEL(KEY_EDITOR), - LABEL(KEY_SPREADSHEET), - LABEL(KEY_GRAPHICSEDITOR), - LABEL(KEY_PRESENTATION), - LABEL(KEY_DATABASE), - LABEL(KEY_NEWS), - LABEL(KEY_VOICEMAIL), - LABEL(KEY_ADDRESSBOOK), - LABEL(KEY_MESSENGER), - LABEL(KEY_DISPLAYTOGGLE), - LABEL(KEY_SPELLCHECK), - LABEL(KEY_LOGOFF), - LABEL(KEY_DOLLAR), - LABEL(KEY_EURO), - LABEL(KEY_FRAMEBACK), - LABEL(KEY_FRAMEFORWARD), - LABEL(KEY_CONTEXT_MENU), - LABEL(KEY_MEDIA_REPEAT), - LABEL(KEY_10CHANNELSUP), - LABEL(KEY_10CHANNELSDOWN), - LABEL(KEY_IMAGES), - LABEL(KEY_NOTIFICATION_CENTER), - LABEL(KEY_PICKUP_PHONE), - LABEL(KEY_HANGUP_PHONE), - LABEL(KEY_DEL_EOL), - LABEL(KEY_DEL_EOS), - LABEL(KEY_INS_LINE), - LABEL(KEY_DEL_LINE), - LABEL(KEY_FN), - LABEL(KEY_FN_ESC), - LABEL(KEY_FN_F1), - LABEL(KEY_FN_F2), - LABEL(KEY_FN_F3), - LABEL(KEY_FN_F4), - LABEL(KEY_FN_F5), - LABEL(KEY_FN_F6), - LABEL(KEY_FN_F7), - LABEL(KEY_FN_F8), - LABEL(KEY_FN_F9), - LABEL(KEY_FN_F10), - LABEL(KEY_FN_F11), - LABEL(KEY_FN_F12), - LABEL(KEY_FN_1), - LABEL(KEY_FN_2), - LABEL(KEY_FN_D), - LABEL(KEY_FN_E), - LABEL(KEY_FN_F), - LABEL(KEY_FN_S), - LABEL(KEY_FN_B), - LABEL(KEY_FN_RIGHT_SHIFT), - LABEL(KEY_BRL_DOT1), - LABEL(KEY_BRL_DOT2), - LABEL(KEY_BRL_DOT3), - LABEL(KEY_BRL_DOT4), - LABEL(KEY_BRL_DOT5), - LABEL(KEY_BRL_DOT6), - LABEL(KEY_BRL_DOT7), - LABEL(KEY_BRL_DOT8), - LABEL(KEY_BRL_DOT9), - LABEL(KEY_BRL_DOT10), - LABEL(KEY_NUMERIC_0), - LABEL(KEY_NUMERIC_1), - LABEL(KEY_NUMERIC_2), - LABEL(KEY_NUMERIC_3), - LABEL(KEY_NUMERIC_4), - LABEL(KEY_NUMERIC_5), - LABEL(KEY_NUMERIC_6), - LABEL(KEY_NUMERIC_7), - LABEL(KEY_NUMERIC_8), - LABEL(KEY_NUMERIC_9), - LABEL(KEY_NUMERIC_STAR), - LABEL(KEY_NUMERIC_POUND), - LABEL(KEY_NUMERIC_A), - LABEL(KEY_NUMERIC_B), - LABEL(KEY_NUMERIC_C), - LABEL(KEY_NUMERIC_D), - LABEL(KEY_CAMERA_FOCUS), - LABEL(KEY_WPS_BUTTON), - LABEL(KEY_TOUCHPAD_TOGGLE), - LABEL(KEY_TOUCHPAD_ON), - LABEL(KEY_TOUCHPAD_OFF), - LABEL(KEY_CAMERA_ZOOMIN), - LABEL(KEY_CAMERA_ZOOMOUT), - LABEL(KEY_CAMERA_UP), - LABEL(KEY_CAMERA_DOWN), - LABEL(KEY_CAMERA_LEFT), - LABEL(KEY_CAMERA_RIGHT), - LABEL(KEY_ATTENDANT_ON), - LABEL(KEY_ATTENDANT_OFF), - LABEL(KEY_ATTENDANT_TOGGLE), - LABEL(KEY_LIGHTS_TOGGLE), - LABEL(BTN_DPAD_UP), - LABEL(BTN_DPAD_DOWN), - LABEL(BTN_DPAD_LEFT), - LABEL(BTN_DPAD_RIGHT), - LABEL(KEY_ALS_TOGGLE), - LABEL(KEY_ROTATE_LOCK_TOGGLE), -// LABEL(KEY_REFRESH_RATE_TOGGLE), - LABEL(KEY_BUTTONCONFIG), - LABEL(KEY_TASKMANAGER), - LABEL(KEY_JOURNAL), - LABEL(KEY_CONTROLPANEL), - LABEL(KEY_APPSELECT), - LABEL(KEY_SCREENSAVER), - LABEL(KEY_VOICECOMMAND), - LABEL(KEY_ASSISTANT), - LABEL(KEY_KBD_LAYOUT_NEXT), - LABEL(KEY_EMOJI_PICKER), - LABEL(KEY_DICTATE), - LABEL(KEY_CAMERA_ACCESS_ENABLE), - LABEL(KEY_CAMERA_ACCESS_DISABLE), - LABEL(KEY_CAMERA_ACCESS_TOGGLE), -// LABEL(KEY_ACCESSIBILITY), -// LABEL(KEY_DO_NOT_DISTURB), - LABEL(KEY_BRIGHTNESS_MIN), - LABEL(KEY_BRIGHTNESS_MAX), - LABEL(KEY_KBDINPUTASSIST_PREV), - LABEL(KEY_KBDINPUTASSIST_NEXT), - LABEL(KEY_KBDINPUTASSIST_PREVGROUP), - LABEL(KEY_KBDINPUTASSIST_NEXTGROUP), - LABEL(KEY_KBDINPUTASSIST_ACCEPT), - LABEL(KEY_KBDINPUTASSIST_CANCEL), - LABEL(KEY_RIGHT_UP), - LABEL(KEY_RIGHT_DOWN), - LABEL(KEY_LEFT_UP), - LABEL(KEY_LEFT_DOWN), - LABEL(KEY_ROOT_MENU), - LABEL(KEY_MEDIA_TOP_MENU), - LABEL(KEY_NUMERIC_11), - LABEL(KEY_NUMERIC_12), - LABEL(KEY_AUDIO_DESC), - LABEL(KEY_3D_MODE), - LABEL(KEY_NEXT_FAVORITE), - LABEL(KEY_STOP_RECORD), - LABEL(KEY_PAUSE_RECORD), - LABEL(KEY_VOD), - LABEL(KEY_UNMUTE), - LABEL(KEY_FASTREVERSE), - LABEL(KEY_SLOWREVERSE), - LABEL(KEY_DATA), - LABEL(KEY_ONSCREEN_KEYBOARD), - LABEL(KEY_PRIVACY_SCREEN_TOGGLE), - LABEL(KEY_SELECTIVE_SCREENSHOT), - LABEL(KEY_NEXT_ELEMENT), - LABEL(KEY_PREVIOUS_ELEMENT), - LABEL(KEY_AUTOPILOT_ENGAGE_TOGGLE), - LABEL(KEY_MARK_WAYPOINT), - LABEL(KEY_SOS), - LABEL(KEY_NAV_CHART), - LABEL(KEY_FISHING_CHART), - LABEL(KEY_SINGLE_RANGE_RADAR), - LABEL(KEY_DUAL_RANGE_RADAR), - LABEL(KEY_RADAR_OVERLAY), - LABEL(KEY_TRADITIONAL_SONAR), - LABEL(KEY_CLEARVU_SONAR), - LABEL(KEY_SIDEVU_SONAR), - LABEL(KEY_NAV_INFO), - LABEL(KEY_BRIGHTNESS_MENU), - LABEL(KEY_MACRO1), - LABEL(KEY_MACRO2), - LABEL(KEY_MACRO3), - LABEL(KEY_MACRO4), - LABEL(KEY_MACRO5), - LABEL(KEY_MACRO6), - LABEL(KEY_MACRO7), - LABEL(KEY_MACRO8), - LABEL(KEY_MACRO9), - LABEL(KEY_MACRO10), - LABEL(KEY_MACRO11), - LABEL(KEY_MACRO12), - LABEL(KEY_MACRO13), - LABEL(KEY_MACRO14), - LABEL(KEY_MACRO15), - LABEL(KEY_MACRO16), - LABEL(KEY_MACRO17), - LABEL(KEY_MACRO18), - LABEL(KEY_MACRO19), - LABEL(KEY_MACRO20), - LABEL(KEY_MACRO21), - LABEL(KEY_MACRO22), - LABEL(KEY_MACRO23), - LABEL(KEY_MACRO24), - LABEL(KEY_MACRO25), - LABEL(KEY_MACRO26), - LABEL(KEY_MACRO27), - LABEL(KEY_MACRO28), - LABEL(KEY_MACRO29), - LABEL(KEY_MACRO30), - LABEL(KEY_MACRO_RECORD_START), - LABEL(KEY_MACRO_RECORD_STOP), - LABEL(KEY_MACRO_PRESET_CYCLE), - LABEL(KEY_MACRO_PRESET1), - LABEL(KEY_MACRO_PRESET2), - LABEL(KEY_MACRO_PRESET3), - LABEL(KEY_KBD_LCD_MENU1), - LABEL(KEY_KBD_LCD_MENU2), - LABEL(KEY_KBD_LCD_MENU3), - LABEL(KEY_KBD_LCD_MENU4), - LABEL(KEY_KBD_LCD_MENU5), - LABEL(BTN_TRIGGER_HAPPY), - LABEL(BTN_TRIGGER_HAPPY1), - LABEL(BTN_TRIGGER_HAPPY2), - LABEL(BTN_TRIGGER_HAPPY3), - LABEL(BTN_TRIGGER_HAPPY4), - LABEL(BTN_TRIGGER_HAPPY5), - LABEL(BTN_TRIGGER_HAPPY6), - LABEL(BTN_TRIGGER_HAPPY7), - LABEL(BTN_TRIGGER_HAPPY8), - LABEL(BTN_TRIGGER_HAPPY9), - LABEL(BTN_TRIGGER_HAPPY10), - LABEL(BTN_TRIGGER_HAPPY11), - LABEL(BTN_TRIGGER_HAPPY12), - LABEL(BTN_TRIGGER_HAPPY13), - LABEL(BTN_TRIGGER_HAPPY14), - LABEL(BTN_TRIGGER_HAPPY15), - LABEL(BTN_TRIGGER_HAPPY16), - LABEL(BTN_TRIGGER_HAPPY17), - LABEL(BTN_TRIGGER_HAPPY18), - LABEL(BTN_TRIGGER_HAPPY19), - LABEL(BTN_TRIGGER_HAPPY20), - LABEL(BTN_TRIGGER_HAPPY21), - LABEL(BTN_TRIGGER_HAPPY22), - LABEL(BTN_TRIGGER_HAPPY23), - LABEL(BTN_TRIGGER_HAPPY24), - LABEL(BTN_TRIGGER_HAPPY25), - LABEL(BTN_TRIGGER_HAPPY26), - LABEL(BTN_TRIGGER_HAPPY27), - LABEL(BTN_TRIGGER_HAPPY28), - LABEL(BTN_TRIGGER_HAPPY29), - LABEL(BTN_TRIGGER_HAPPY30), - LABEL(BTN_TRIGGER_HAPPY31), - LABEL(BTN_TRIGGER_HAPPY32), - LABEL(BTN_TRIGGER_HAPPY33), - LABEL(BTN_TRIGGER_HAPPY34), - LABEL(BTN_TRIGGER_HAPPY35), - LABEL(BTN_TRIGGER_HAPPY36), - LABEL(BTN_TRIGGER_HAPPY37), - LABEL(BTN_TRIGGER_HAPPY38), - LABEL(BTN_TRIGGER_HAPPY39), - LABEL(BTN_TRIGGER_HAPPY40), - LABEL(KEY_MAX), - LABEL_END, - }; - static struct label rel_labels[] = { - LABEL(REL_X), - LABEL(REL_Y), - LABEL(REL_Z), - LABEL(REL_RX), - LABEL(REL_RY), - LABEL(REL_RZ), - LABEL(REL_HWHEEL), - LABEL(REL_DIAL), - LABEL(REL_WHEEL), - LABEL(REL_MISC), - LABEL(REL_RESERVED), - LABEL(REL_WHEEL_HI_RES), - LABEL(REL_HWHEEL_HI_RES), - LABEL(REL_MAX), - LABEL_END, - }; - static struct label abs_labels[] = { - LABEL(ABS_X), - LABEL(ABS_Y), - LABEL(ABS_Z), - LABEL(ABS_RX), - LABEL(ABS_RY), - LABEL(ABS_RZ), - LABEL(ABS_THROTTLE), - LABEL(ABS_RUDDER), - LABEL(ABS_WHEEL), - LABEL(ABS_GAS), - LABEL(ABS_BRAKE), - LABEL(ABS_HAT0X), - LABEL(ABS_HAT0Y), - LABEL(ABS_HAT1X), - LABEL(ABS_HAT1Y), - LABEL(ABS_HAT2X), - LABEL(ABS_HAT2Y), - LABEL(ABS_HAT3X), - LABEL(ABS_HAT3Y), - LABEL(ABS_PRESSURE), - LABEL(ABS_DISTANCE), - LABEL(ABS_TILT_X), - LABEL(ABS_TILT_Y), - LABEL(ABS_TOOL_WIDTH), - LABEL(ABS_VOLUME), - LABEL(ABS_PROFILE), - LABEL(ABS_MISC), - LABEL(ABS_RESERVED), - LABEL(ABS_MT_SLOT), - LABEL(ABS_MT_TOUCH_MAJOR), - LABEL(ABS_MT_TOUCH_MINOR), - LABEL(ABS_MT_WIDTH_MAJOR), - LABEL(ABS_MT_WIDTH_MINOR), - LABEL(ABS_MT_ORIENTATION), - LABEL(ABS_MT_POSITION_X), - LABEL(ABS_MT_POSITION_Y), - LABEL(ABS_MT_TOOL_TYPE), - LABEL(ABS_MT_BLOB_ID), - LABEL(ABS_MT_TRACKING_ID), - LABEL(ABS_MT_PRESSURE), - LABEL(ABS_MT_DISTANCE), - LABEL(ABS_MT_TOOL_X), - LABEL(ABS_MT_TOOL_Y), - LABEL(ABS_MAX), - LABEL_END, - }; - static struct label sw_labels[] = { - LABEL(SW_LID), - LABEL(SW_TABLET_MODE), - LABEL(SW_HEADPHONE_INSERT), - LABEL(SW_RFKILL_ALL), - LABEL(SW_MICROPHONE_INSERT), - LABEL(SW_DOCK), - LABEL(SW_LINEOUT_INSERT), - LABEL(SW_JACK_PHYSICAL_INSERT), - LABEL(SW_VIDEOOUT_INSERT), - LABEL(SW_CAMERA_LENS_COVER), - LABEL(SW_KEYPAD_SLIDE), - LABEL(SW_FRONT_PROXIMITY), - LABEL(SW_ROTATE_LOCK), - LABEL(SW_LINEIN_INSERT), - LABEL(SW_MUTE_DEVICE), - LABEL(SW_PEN_INSERTED), - LABEL(SW_MACHINE_COVER), - LABEL(SW_MAX), - LABEL_END, - }; - static struct label msc_labels[] = { - LABEL(MSC_SERIAL), - LABEL(MSC_PULSELED), - LABEL(MSC_GESTURE), - LABEL(MSC_RAW), - LABEL(MSC_SCAN), - LABEL(MSC_TIMESTAMP), - LABEL(MSC_MAX), - LABEL_END, - }; - static struct label led_labels[] = { - LABEL(LED_NUML), - LABEL(LED_CAPSL), - LABEL(LED_SCROLLL), - LABEL(LED_COMPOSE), - LABEL(LED_KANA), - LABEL(LED_SLEEP), - LABEL(LED_SUSPEND), - LABEL(LED_MUTE), - LABEL(LED_MISC), - LABEL(LED_MAIL), - LABEL(LED_CHARGING), - LABEL(LED_MAX), - LABEL_END, - }; - static struct label rep_labels[] = { - LABEL(REP_DELAY), - LABEL(REP_PERIOD), - LABEL(REP_MAX), - LABEL_END, - }; - static struct label snd_labels[] = { - LABEL(SND_CLICK), - LABEL(SND_BELL), - LABEL(SND_TONE), - LABEL(SND_MAX), - LABEL_END, - }; - static struct label mt_tool_labels[] = { - LABEL(MT_TOOL_FINGER), - LABEL(MT_TOOL_PEN), - LABEL(MT_TOOL_PALM), - LABEL(MT_TOOL_DIAL), - LABEL(MT_TOOL_MAX), - LABEL_END, - }; - static struct label ff_status_labels[] = { - LABEL(FF_STATUS_STOPPED), - LABEL(FF_STATUS_PLAYING), - LABEL(FF_STATUS_MAX), - LABEL_END, - }; - static struct label ff_labels[] = { - LABEL(FF_RUMBLE), - LABEL(FF_PERIODIC), - LABEL(FF_CONSTANT), - LABEL(FF_SPRING), - LABEL(FF_FRICTION), - LABEL(FF_DAMPER), - LABEL(FF_INERTIA), - LABEL(FF_RAMP), - LABEL(FF_SQUARE), - LABEL(FF_TRIANGLE), - LABEL(FF_SINE), - LABEL(FF_SAW_UP), - LABEL(FF_SAW_DOWN), - LABEL(FF_CUSTOM), - LABEL(FF_GAIN), - LABEL(FF_AUTOCENTER), - LABEL(FF_MAX), - LABEL_END, - }; - -#undef LABEL -#undef LABEL_END - - std::string getLabel(const label *labels, int value) { - if (labels == nullptr) return std::to_string(value); - while (labels->name != nullptr && value != labels->value) { - labels++; - } - return labels->name != nullptr ? labels->name : std::to_string(value); - } - - std::optional getValue(const label *labels, const char *searchLabel) { - if (labels == nullptr) return {}; - while (labels->name != nullptr && ::strcasecmp(labels->name, searchLabel) != 0) { - labels++; - } - return labels->name != nullptr ? std::make_optional(labels->value) : std::nullopt; - } - - const label *getCodeLabelsForType(int32_t type) { - switch (type) { - case EV_SYN: - return syn_labels; - case EV_KEY: - return key_labels; - case EV_REL: - return rel_labels; - case EV_ABS: - return abs_labels; - case EV_SW: - return sw_labels; - case EV_MSC: - return msc_labels; - case EV_LED: - return led_labels; - case EV_REP: - return rep_labels; - case EV_SND: - return snd_labels; - case EV_FF: - return ff_labels; - case EV_FF_STATUS: - return ff_status_labels; - default: - return nullptr; - } - } - - const label *getValueLabelsForTypeAndCode(int32_t type, int32_t code) { - if (type == EV_KEY) { - return ev_key_value_labels; - } - if (type == EV_ABS && code == ABS_MT_TOOL_TYPE) { - return mt_tool_labels; - } - return nullptr; - } - - } // namespace - - EvdevEventLabel - InputEventLookup::getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value) { - return { - .type = getLabel(ev_labels, type), - .code = getLabel(getCodeLabelsForType(type), code), - .value = getLabel(getValueLabelsForTypeAndCode(type, code), value), - }; - } - - std::optional InputEventLookup::getLinuxEvdevEventTypeByLabel(const char *label) { - return getValue(ev_labels, label); - } - - std::optional InputEventLookup::getLinuxEvdevEventCodeByLabel(int32_t type, - const char *label) { - return getValue(getCodeLabelsForType(type), label); - } - - std::optional InputEventLookup::getLinuxEvdevInputPropByLabel(const char *label) { - return getValue(input_prop_labels, label); - } - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/InputEventLabels.h b/sysbridge/src/main/cpp/android/input/InputEventLabels.h deleted file mode 100644 index 066ff06ca7..0000000000 --- a/sysbridge/src/main/cpp/android/input/InputEventLabels.h +++ /dev/null @@ -1,101 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -namespace android { - - template - size_t size(T (&)[N]) { return N; } - - struct InputEventLabel { - const char *literal; - int value; - }; - - struct EvdevEventLabel { - std::string type; - std::string code; - std::string value; - }; - -// NOTE: If you want a new key code, axis code, led code or flag code in keylayout file, -// then you must add it to InputEventLabels.cpp. - - class InputEventLookup { - /** - * This class is not purely static, but uses a singleton pattern in order to delay the - * initialization of the maps that it contains. If it were purely static, the maps could be - * created early, and would cause sanitizers to report memory leaks. - */ - public: - InputEventLookup(InputEventLookup& other) = delete; - - void operator=(const InputEventLookup&) = delete; - - static std::optional lookupValueByLabel(const std::unordered_map& map, - const char* literal); - - static const char* lookupLabelByValue(const std::vector& vec, int value); - - static std::optional getKeyCodeByLabel(const char* label); - - static const char* getLabelByKeyCode(int32_t keyCode); - - static std::optional getKeyFlagByLabel(const char* label); - - static std::optional getAxisByLabel(const char* label); - - static const char* getAxisLabel(int32_t axisId); - - static std::optional getLedByLabel(const char* label); - - static EvdevEventLabel getLinuxEvdevLabel(int32_t type, int32_t code, int32_t value); - - static std::optional getLinuxEvdevEventTypeByLabel(const char* label); - - static std::optional getLinuxEvdevEventCodeByLabel(int32_t type, const char* label); - - static std::optional getLinuxEvdevInputPropByLabel(const char* label); - - private: - InputEventLookup(); - - static const InputEventLookup& get() { - static InputEventLookup sLookup; - return sLookup; - } - - const std::unordered_map KEYCODES; - - const std::vector KEY_NAMES; - - const std::unordered_map AXES; - - const std::vector AXES_NAMES; - - const std::unordered_map LEDS; - - const std::unordered_map FLAGS; - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp b/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp deleted file mode 100644 index bbf5a5437b..0000000000 --- a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.cpp +++ /dev/null @@ -1,444 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, Tokenizer.cppeither express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../logging.h" -#include -#include "../utils/String8.h" -#include "KeyLayoutMap.h" -#include "../utils/Tokenizer.h" -#include "InputEventLabels.h" -#include - -#include -#include -#include -#include "../libbase/result.h" -#include "../liblog/log_main.h" -#include "Input.h" - -#define DEBUG_MAPPING false -#define DEBUG_PARSER false - -// Enables debug output for parser performance. -#define DEBUG_PARSER_PERFORMANCE 0 - -namespace android { - - namespace { - - std::optional parseInt(const char *str) { - char *end; - errno = 0; - const int value = strtol(str, &end, 0); - if (end == str) { - LOGE("Could not parse %s", str); - return {}; - } - if (errno == ERANGE) { - LOGE("Out of bounds: %s", str); - return {}; - } - return value; - } - - constexpr const char *WHITESPACE = " \t\r"; - - } // namespace - - KeyLayoutMap::KeyLayoutMap() = default; - - KeyLayoutMap::~KeyLayoutMap() = default; - - base::Result> - KeyLayoutMap::loadContents(const std::string &filename, - const char *contents) { - return load(filename, contents); - } - - base::Result> KeyLayoutMap::load(const std::string &filename, - const char *contents) { - Tokenizer *tokenizer; - status_t status; - if (contents == nullptr) { - status = Tokenizer::open(String8(filename.c_str()), &tokenizer); - } else { - status = Tokenizer::fromContents(String8(filename.c_str()), contents, &tokenizer); - } - - if (status) { - LOGE("Error %d opening key layout map file %s.", status, filename.c_str()); - return Errorf("Error {} opening key layout map file {}.", status, filename.c_str()); - } - std::unique_ptr t(tokenizer); - auto ret = load(t.get()); - if (!ret.ok()) { - return ret; - } - const std::shared_ptr &map = *ret; - LOG_ALWAYS_FATAL_IF(map == nullptr, "Returned map should not be null if there's no error"); - - map->mLoadFileName = filename; - return ret; - } - - base::Result> KeyLayoutMap::load(Tokenizer *tokenizer) { - std::shared_ptr map = std::shared_ptr(new KeyLayoutMap()); - status_t status = OK; - if (!map.get()) { - LOGE("Error allocating key layout map."); - return Errorf("Error allocating key layout map."); - } else { -#if DEBUG_PARSER_PERFORMANCE - nsecs_t startTime = systemTime(SYSTEM_TIME_MONOTONIC); -#endif - Parser parser(map.get(), tokenizer); - status = parser.parse(); -#if DEBUG_PARSER_PERFORMANCE - nsecs_t elapsedTime = systemTime(SYSTEM_TIME_MONOTONIC) - startTime; - LOGD("Parsed key layout map file '%s' %d lines in %0.3fms.", - tokenizer->getFilename().c_str(), tokenizer->getLineNumber(), - elapsedTime / 1000000.0); -#endif - if (!status) { - return std::move(map); - } - } - return Errorf("Load KeyLayoutMap failed {}.", status); - } - - status_t KeyLayoutMap::mapKey(int32_t scanCode, int32_t usageCode, - int32_t *outKeyCode, uint32_t *outFlags) const { - const Key *key = getKey(scanCode, usageCode); - if (!key) { - ALOGD_IF(DEBUG_MAPPING, "mapKey: scanCode=%d, usageCode=0x%08x ~ Failed.", scanCode, - usageCode); - *outKeyCode = AKEYCODE_UNKNOWN; - *outFlags = 0; - return NAME_NOT_FOUND; - } - - *outKeyCode = key->keyCode; - *outFlags = key->flags; - - ALOGD_IF(DEBUG_MAPPING, - "mapKey: scanCode=%d, usageCode=0x%08x ~ Result keyCode=%d, outFlags=0x%08x.", - scanCode, usageCode, *outKeyCode, *outFlags); - return NO_ERROR; - } - - const KeyLayoutMap::Key *KeyLayoutMap::getKey(int32_t scanCode, int32_t usageCode) const { - if (usageCode) { - auto it = mKeysByUsageCode.find(usageCode); - if (it != mKeysByUsageCode.end()) { - return &it->second; - } - } - if (scanCode) { - auto it = mKeysByScanCode.find(scanCode); - if (it != mKeysByScanCode.end()) { - return &it->second; - } - } - return nullptr; - } - - std::vector KeyLayoutMap::findScanCodesForKey(int32_t keyCode) const { - std::vector scanCodes; - // b/354333072: Only consider keys without FUNCTION flag - for (const auto &[scanCode, key]: mKeysByScanCode) { - if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FUNCTION)) { - scanCodes.push_back(scanCode); - } - } - return scanCodes; - } - - std::vector KeyLayoutMap::findUsageCodesForKey(int32_t keyCode) const { - std::vector usageCodes; - for (const auto &[usageCode, key]: mKeysByUsageCode) { - if (keyCode == key.keyCode && !(key.flags & POLICY_FLAG_FALLBACK_USAGE_MAPPING)) { - usageCodes.push_back(usageCode); - } - } - return usageCodes; - } - - std::optional KeyLayoutMap::mapAxis(int32_t scanCode) const { - auto it = mAxes.find(scanCode); - if (it == mAxes.end()) { - ALOGD_IF(DEBUG_MAPPING, "mapAxis: scanCode=%d ~ Failed.", scanCode); - return std::nullopt; - } - - const AxisInfo &axisInfo = it->second; - ALOGD_IF(DEBUG_MAPPING, - "mapAxis: scanCode=%d ~ Result mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, - axisInfo.flatOverride); - return axisInfo; - } - -// --- KeyLayoutMap::Parser --- - - KeyLayoutMap::Parser::Parser(KeyLayoutMap *map, Tokenizer *tokenizer) : - mMap(map), mTokenizer(tokenizer) { - } - - KeyLayoutMap::Parser::~Parser() { - } - - status_t KeyLayoutMap::Parser::parse() { - while (!mTokenizer->isEof()) { - ALOGD_IF(DEBUG_PARSER, "Parsing %s: '%s'.", mTokenizer->getLocation().c_str(), - mTokenizer->peekRemainderOfLine().c_str()); - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "key") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseKey(); - if (status) return status; - } else if (keywordToken == "axis") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseAxis(); - if (status) return status; - } else if (keywordToken == "led") { - // Skip LEDs, we don't need them for Key Mapper - mTokenizer->nextLine(); - continue; - } else if (keywordToken == "sensor") { - // Skip Sensors, we don't need them for Key Mapper - mTokenizer->nextLine(); - continue; - } else if (keywordToken == "requires_kernel_config") { - mTokenizer->skipDelimiters(WHITESPACE); - status_t status = parseRequiredKernelConfig(); - if (status) return status; - } else { - LOGE("%s: Expected keyword, got '%s'.", mTokenizer->getLocation().c_str(), - keywordToken.c_str()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - - if (!mTokenizer->isEol() && mTokenizer->peekChar() != '#') { - LOGW("%s: Expected end of line or trailing comment, got '%s'.", - mTokenizer->getLocation().c_str(), - mTokenizer->peekRemainderOfLine().c_str()); - return BAD_VALUE; - } - } - - mTokenizer->nextLine(); - } - return NO_ERROR; - } - - status_t KeyLayoutMap::Parser::parseKey() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - bool mapUsage = false; - if (codeToken == "usage") { - mapUsage = true; - mTokenizer->skipDelimiters(WHITESPACE); - codeToken = mTokenizer->nextToken(WHITESPACE); - } - - std::optional code = parseInt(codeToken.c_str()); - if (!code) { - LOGE("%s: Expected key %s number, got '%s'.", mTokenizer->getLocation().c_str(), - mapUsage ? "usage" : "scan code", codeToken.c_str()); - return BAD_VALUE; - } - std::unordered_map &map = - mapUsage ? mMap->mKeysByUsageCode : mMap->mKeysByScanCode; - if (map.find(*code) != map.end()) { - LOGE("%s: Duplicate entry for key %s '%s'.", mTokenizer->getLocation().c_str(), - mapUsage ? "usage" : "scan code", codeToken.c_str()); - return BAD_VALUE; - } - - mTokenizer->skipDelimiters(WHITESPACE); - String8 keyCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional keyCode = InputEventLookup::getKeyCodeByLabel(keyCodeToken.c_str()); - - if (!keyCode) { -// LOGW("%s: Unknown key code label %s", mTokenizer->getLocation().c_str(), -// keyCodeToken.c_str()); - // Do not crash at this point because there may be more flags afterwards that need parsing. - } - - uint32_t flags = 0; - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') break; - - String8 flagToken = mTokenizer->nextToken(WHITESPACE); - std::optional flag = InputEventLookup::getKeyFlagByLabel(flagToken.c_str()); - if (!flag) { - LOGE("%s: Expected key flag label, got '%s'.", mTokenizer->getLocation().c_str(), - flagToken.c_str()); - return BAD_VALUE; - } - if (flags & *flag) { - LOGE("%s: Duplicate key flag '%s'.", mTokenizer->getLocation().c_str(), - flagToken.c_str()); - return BAD_VALUE; - } - flags |= *flag; - } - - ALOGD_IF(DEBUG_PARSER, "Parsed key %s: code=%d, keyCode=%d, flags=0x%08x.", - mapUsage ? "usage" : "scan code", *code, *keyCode, flags); - - // The key code may be unknown so only insert a key if it is known. - if (keyCode) { - Key key; - key.keyCode = *keyCode; - key.flags = flags; - map.insert({*code, key}); - } - - return NO_ERROR; - } - - status_t KeyLayoutMap::Parser::parseAxis() { - String8 scanCodeToken = mTokenizer->nextToken(WHITESPACE); - std::optional scanCode = parseInt(scanCodeToken.c_str()); - if (!scanCode) { - LOGE("%s: Expected axis scan code number, got '%s'.", mTokenizer->getLocation().c_str(), - scanCodeToken.c_str()); - return BAD_VALUE; - } - if (mMap->mAxes.find(*scanCode) != mMap->mAxes.end()) { - LOGE("%s: Duplicate entry for axis scan code '%s'.", mTokenizer->getLocation().c_str(), - scanCodeToken.c_str()); - return BAD_VALUE; - } - - AxisInfo axisInfo; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 token = mTokenizer->nextToken(WHITESPACE); - if (token == "invert") { - axisInfo.mode = AxisInfo::MODE_INVERT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 axisToken = mTokenizer->nextToken(WHITESPACE); - std::optional axis = InputEventLookup::getAxisByLabel(axisToken.c_str()); - if (!axis) { - LOGE("%s: Expected inverted axis label, got '%s'.", - mTokenizer->getLocation().c_str(), - axisToken.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - } else if (token == "split") { - axisInfo.mode = AxisInfo::MODE_SPLIT; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 splitToken = mTokenizer->nextToken(WHITESPACE); - std::optional splitValue = parseInt(splitToken.c_str()); - if (!splitValue) { - LOGE("%s: Expected split value, got '%s'.", mTokenizer->getLocation().c_str(), - splitToken.c_str()); - return BAD_VALUE; - } - axisInfo.splitValue = *splitValue; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 lowAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional axis = InputEventLookup::getAxisByLabel(lowAxisToken.c_str()); - if (!axis) { - LOGE("%s: Expected low axis label, got '%s'.", mTokenizer->getLocation().c_str(), - lowAxisToken.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - - mTokenizer->skipDelimiters(WHITESPACE); - String8 highAxisToken = mTokenizer->nextToken(WHITESPACE); - std::optional highAxis = InputEventLookup::getAxisByLabel(highAxisToken.c_str()); - if (!highAxis) { - LOGE("%s: Expected high axis label, got '%s'.", mTokenizer->getLocation().c_str(), - highAxisToken.c_str()); - return BAD_VALUE; - } - axisInfo.highAxis = *highAxis; - } else { - std::optional axis = InputEventLookup::getAxisByLabel(token.c_str()); - if (!axis) { - LOGE("%s: Expected axis label, 'split' or 'invert', got '%s'.", - mTokenizer->getLocation().c_str(), token.c_str()); - return BAD_VALUE; - } - axisInfo.axis = *axis; - } - - for (;;) { - mTokenizer->skipDelimiters(WHITESPACE); - if (mTokenizer->isEol() || mTokenizer->peekChar() == '#') { - break; - } - String8 keywordToken = mTokenizer->nextToken(WHITESPACE); - if (keywordToken == "flat") { - mTokenizer->skipDelimiters(WHITESPACE); - String8 flatToken = mTokenizer->nextToken(WHITESPACE); - std::optional flatOverride = parseInt(flatToken.c_str()); - if (!flatOverride) { - LOGE("%s: Expected flat value, got '%s'.", mTokenizer->getLocation().c_str(), - flatToken.c_str()); - return BAD_VALUE; - } - axisInfo.flatOverride = *flatOverride; - } else { - LOGE("%s: Expected keyword 'flat', got '%s'.", mTokenizer->getLocation().c_str(), - keywordToken.c_str()); - return BAD_VALUE; - } - } - - ALOGD_IF(DEBUG_PARSER, - "Parsed axis: scanCode=%d, mode=%d, axis=%d, highAxis=%d, " - "splitValue=%d, flatOverride=%d.", - *scanCode, axisInfo.mode, axisInfo.axis, axisInfo.highAxis, axisInfo.splitValue, - axisInfo.flatOverride); - mMap->mAxes.insert({*scanCode, axisInfo}); - return NO_ERROR; - } - -// Parse the name of a required kernel config. -// The layout won't be used if the specified kernel config is not present -// Examples: -// requires_kernel_config CONFIG_HID_PLAYSTATION - status_t KeyLayoutMap::Parser::parseRequiredKernelConfig() { - String8 codeToken = mTokenizer->nextToken(WHITESPACE); - std::string configName = codeToken.c_str(); - - const auto result = mMap->mRequiredKernelConfigs.emplace(configName); - if (!result.second) { - LOGE("%s: Duplicate entry for required kernel config %s.", - mTokenizer->getLocation().c_str(), configName.c_str()); - return BAD_VALUE; - } - -// ALOGD_IF(DEBUG_PARSER, "Parsed required kernel config: name=%s", configName.c_str()); - return NO_ERROR; - } -} // namespace android diff --git a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h b/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h deleted file mode 100644 index f5646f33e3..0000000000 --- a/sysbridge/src/main/cpp/android/input/KeyLayoutMap.h +++ /dev/null @@ -1,123 +0,0 @@ -/* - * Copyright (C) 2008 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include "../utils/Tokenizer.h" -#include "../libbase/result.h" -#include - -namespace android { - - struct AxisInfo { - enum Mode { - // Axis value is reported directly. - MODE_NORMAL = 0, - // Axis value should be inverted before reporting. - MODE_INVERT = 1, - // Axis value should be split into two axes - MODE_SPLIT = 2, - }; - - // Axis mode. - Mode mode; - - // Axis id. - // When split, this is the axis used for values smaller than the split position. - int32_t axis; - - // When split, this is the axis used for values after higher than the split position. - int32_t highAxis; - - // The split value, or 0 if not split. - int32_t splitValue; - - // The flat value, or -1 if none. - int32_t flatOverride; - - AxisInfo() : mode(MODE_NORMAL), axis(-1), highAxis(-1), splitValue(0), flatOverride(-1) { - } - }; - -/** - * Describes a mapping from keyboard scan codes and joystick axes to Android key codes and axes. - * - * This object is immutable after it has been loaded. - */ - class KeyLayoutMap { - public: - static base::Result> load(const std::string &filename, - const char *contents = nullptr); - - static base::Result> loadContents(const std::string &filename, - const char *contents); - - status_t mapKey(int32_t scanCode, int32_t usageCode, - int32_t *outKeyCode, uint32_t *outFlags) const; - - std::vector findScanCodesForKey(int32_t keyCode) const; - - std::optional findScanCodeForLed(int32_t ledCode) const; - - std::vector findUsageCodesForKey(int32_t keyCode) const; - - std::optional findUsageCodeForLed(int32_t ledCode) const; - - std::optional mapAxis(int32_t scanCode) const; - - virtual ~KeyLayoutMap(); - - private: - static base::Result> load(Tokenizer *tokenizer); - - struct Key { - int32_t keyCode; - uint32_t flags; - }; - - std::unordered_map mKeysByScanCode; - std::unordered_map mKeysByUsageCode; - std::unordered_map mAxes; - std::set mRequiredKernelConfigs; - std::string mLoadFileName; - - KeyLayoutMap(); - - const Key *getKey(int32_t scanCode, int32_t usageCode) const; - - class Parser { - KeyLayoutMap *mMap; - Tokenizer *mTokenizer; - - public: - Parser(KeyLayoutMap *map, Tokenizer *tokenizer); - - ~Parser(); - - status_t parse(); - - private: - status_t parseKey(); - - status_t parseAxis(); - - status_t parseRequiredKernelConfig(); - }; - }; - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/errors.h b/sysbridge/src/main/cpp/android/libbase/errors.h deleted file mode 100644 index 0ad4ecabd8..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/errors.h +++ /dev/null @@ -1,154 +0,0 @@ -/* - * Copyright (C) 2016 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Portable error handling functions. This is only necessary for host-side -// code that needs to be cross-platform; code that is only run on Unix should -// just use errno and strerror() for simplicity. -// -// There is some complexity since Windows has (at least) three different error -// numbers, not all of which share the same type: -// * errno: for C runtime errors. -// * GetLastError(): Windows non-socket errors. -// * WSAGetLastError(): Windows socket errors. -// errno can be passed to strerror() on all platforms, but the other two require -// special handling to get the error string. Refer to Microsoft documentation -// to determine which error code to check for each function. - -#pragma once - -#include - -#include - -namespace android { -namespace base { - -// Returns a string describing the given system error code. |error_code| must -// be errno on Unix or GetLastError()/WSAGetLastError() on Windows. Passing -// errno on Windows has undefined behavior. -std::string SystemErrorCodeToString(int error_code); - -} // namespace base -} // namespace android - -// Convenient macros for evaluating a statement, checking if the result is error, and returning it -// to the caller. If it is ok then the inner value is unwrapped (if applicable) and returned. -// -// Usage with Result: -// -// Result getFoo() {...} -// -// Result getBar() { -// Foo foo = OR_RETURN(getFoo()); -// return Bar{foo}; -// } -// -// Usage with status_t: -// -// status_t getFoo(Foo*) {...} -// -// status_t getBar(Bar* bar) { -// Foo foo; -// OR_RETURN(getFoo(&foo)); -// *bar = Bar{foo}; -// return OK; -// } -// -// Actually this can be used for any type as long as the OkOrFail contract is satisfied. See -// below. -// If implicit conversion compilation errors occur involving a value type with a templated -// forwarding ref ctor, compilation with cpp20 or explicitly converting to the desired -// return type is required. -#define OR_RETURN(expr) \ - UNWRAP_OR_DO(__or_return_expr, expr, { return ok_or_fail::Fail(std::move(__or_return_expr)); }) - -// Same as OR_RETURN, but aborts if expr is a failure. -#if defined(__BIONIC__) -#define OR_FATAL(expr) \ - UNWRAP_OR_DO(__or_fatal_expr, expr, { \ - __assert(__FILE__, __LINE__, ok_or_fail::ErrorMessage(__or_fatal_expr).c_str()); \ - }) -#else -#define OR_FATAL(expr) \ - UNWRAP_OR_DO(__or_fatal_expr, expr, { \ - fprintf(stderr, "%s:%d: assertion \"%s\" failed", __FILE__, __LINE__, \ - ok_or_fail::ErrorMessage(__or_fatal_expr).c_str()); \ - abort(); \ - }) -#endif - -// Variant for use in gtests, which aborts the test function with an assertion failure on error. -// This is akin to ASSERT_OK_AND_ASSIGN for absl::Status, except the assignment is external. It -// assumes the user depends on libgmock and includes gtest/gtest.h. -#define OR_ASSERT_FAIL(expr) \ - UNWRAP_OR_DO(__or_assert_expr, expr, { \ - FAIL() << "Value of: " << #expr << "\n" \ - << " Actual: " << __or_assert_expr.error().message() << "\n" \ - << "Expected: is ok\n"; \ - }) - -// Generic macro to execute any statement(s) on error. Execution should never reach the end of them. -// result_var is assigned expr and is only visible to on_error_stmts. -#define UNWRAP_OR_DO(result_var, expr, on_error_stmts) \ - ({ \ - decltype(expr)&& result_var = (expr); \ - typedef android::base::OkOrFail> ok_or_fail; \ - if (!ok_or_fail::IsOk(result_var)) { \ - { \ - on_error_stmts; \ - } \ - __builtin_unreachable(); \ - } \ - ok_or_fail::Unwrap(std::move(result_var)); \ - }) - -namespace android { -namespace base { - -// The OkOrFail contract for a type T. This must be implemented for a type T if you want to use -// OR_RETURN(stmt) where stmt evalues to a value of type T. -template -struct OkOrFail { - // Checks if T is ok or fail. - static bool IsOk(const T&); - - // Turns T into the success value. - template - static U Unwrap(T&&); - - // Moves T into OkOrFail, so that we can convert it to other types - OkOrFail(T&& v); - OkOrFail() = delete; - OkOrFail(const T&) = delete; - - // And there need to be one or more conversion operators that turns the error value of T into a - // target type. For example, for T = Result, there can be ... - // - // // for the case where OR_RETURN is called in a function expecting E - // operator E()&& { return val_.error().code(); } - // - // // for the case where OR_RETURN is called in a function expecting Result - // template - // operator Result()&& { return val_.error(); } - - // And there needs to be a method that returns the string representation of the fail value. - // static const std::string& ErrorMessage(const T& v); - // or - // static std::string ErrorMessage(const T& v); -}; - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/expected.h b/sysbridge/src/main/cpp/android/libbase/expected.h deleted file mode 100644 index ddab9e5b57..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/expected.h +++ /dev/null @@ -1,622 +0,0 @@ -/* - * Copyright (C) 2019 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include -#include - -// android::base::expected is a partial implementation of C++23's std::expected -// for Android. -// -// Usage: -// using android::base::expected; -// using android::base::unexpected; -// -// expected safe_divide(double i, double j) { -// if (j == 0) return unexpected("divide by zero"); -// else return i / j; -// } -// -// void test() { -// auto q = safe_divide(10, 0); -// if (q.ok()) { printf("%f\n", q.value()); } -// else { printf("%s\n", q.error().c_str()); } -// } -// -// Once the Android platform has moved to C++23, this will be removed and -// android::base::expected will be type aliased to std::expected. -// - -namespace android { -namespace base { - -// Synopsis -template -class expected; - -template -class unexpected; -template -unexpected(E) -> unexpected; - -template -class bad_expected_access; - -template <> -class bad_expected_access; - -struct unexpect_t { - explicit unexpect_t() = default; -}; -inline constexpr unexpect_t unexpect{}; - -// macros for SFINAE -#define _ENABLE_IF(...) \ - , std::enable_if_t<(__VA_ARGS__)>* = nullptr - -// Define NODISCARD_EXPECTED to prevent expected from being -// ignored when used as a return value. This is off by default. -#ifdef NODISCARD_EXPECTED -#define _NODISCARD_ [[nodiscard]] -#else -#define _NODISCARD_ -#endif - -#define _EXPLICIT(cond) \ - _Pragma("clang diagnostic push") \ - _Pragma("clang diagnostic ignored \"-Wc++20-extensions\"") explicit(cond) \ - _Pragma("clang diagnostic pop") - -#define _COMMA , - -namespace expected_internal { - -template -struct remove_cvref { - using type = std::remove_cv_t>; -}; - -template -using remove_cvref_t = typename remove_cvref::type; - -// Can T be constructed from W (or W converted to T)? W can be lvalue or rvalue, -// const or not. -template -inline constexpr bool converts_from_any_cvref = - std::disjunction_v, std::is_convertible, - std::is_constructible, std::is_convertible, - std::is_constructible, std::is_convertible, - std::is_constructible, std::is_convertible>; - -template -struct is_expected : std::false_type {}; - -template -struct is_expected> : std::true_type {}; - -template -inline constexpr bool is_expected_v = is_expected::value; - -template -struct is_unexpected : std::false_type {}; - -template -struct is_unexpected> : std::true_type {}; - -template -inline constexpr bool is_unexpected_v = is_unexpected::value; - -// Constraints on constructing an expected from an expected -// related to T and U. UF is either "const U&" or "U". -template -inline constexpr bool convert_value_constraints = - std::is_constructible_v && - (std::is_same_v, bool> || !converts_from_any_cvref>); - -// Constraints on constructing an expected<..., E> from an expected -// related to E, G, and expected. GF is either "const G&" or "G". -template -inline constexpr bool convert_error_constraints = - std::is_constructible_v && - !std::is_constructible_v, expected&> && - !std::is_constructible_v, expected> && - !std::is_constructible_v, const expected&> && - !std::is_constructible_v, const expected>; - -// If an exception is thrown in expected::operator=, while changing the expected -// object between a value and an error, the expected object is supposed to -// retain its original value, which is only possible if certain constructors -// are noexcept. This implementation doesn't try to be exception-safe, but -// enforce these constraints anyway because std::expected also will enforce -// them, and we intend to switch to it eventually. -template -inline constexpr bool eh_assign_constraints = - std::is_nothrow_constructible_v || - std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v; - -// Implement expected<..., E>::expected([const] unexpected [&/&&]). -#define _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(GF, ParamType, forward_func) \ - template )> \ - constexpr _EXPLICIT((!std::is_convertible_v)) \ - expected(ParamType e) noexcept(std::is_nothrow_constructible_v) \ - : var_(std::in_place_index<1>, forward_func(e.error())) {} - -// Implement expected<..., E>::operator=([const] unexpected [&/&&]). -#define _ASSIGN_UNEXPECTED_TO_EXPECTED(GF, ParamType, forward_func, extra_constraints) \ - template && \ - std::is_assignable_v) && \ - extra_constraints> \ - constexpr expected& operator=(ParamType e) noexcept(std::is_nothrow_constructible_v && \ - std::is_nothrow_assignable_v) { \ - if (has_value()) { \ - var_.template emplace<1>(forward_func(e.error())); \ - } else { \ - error() = forward_func(e.error()); \ - } \ - return *this; \ - } - -} // namespace expected_internal - -// Class expected -template -class _NODISCARD_ expected { - static_assert(std::is_object_v && !std::is_array_v && - !std::is_same_v, std::in_place_t> && - !std::is_same_v, unexpect_t> && - !expected_internal::is_unexpected_v>, - "expected value type cannot be a reference, a function, an array, in_place_t, " - "unexpect_t, or unexpected"); - - public: - using value_type = T; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // Delegate simple operations to the underlying std::variant. std::variant - // doesn't set noexcept well, at least for copy ctor/assign, so set it - // explicitly. Technically the copy/move assignment operators should also be - // deleted if neither T nor E satisfies is_nothrow_move_constructible_v, but - // that would require making these operator= methods into template functions. - constexpr expected() = default; - constexpr expected(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_constructible_v) = default; - constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_move_constructible_v) = default; - constexpr expected& operator=(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v && - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; - constexpr expected& operator=(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v && - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; - - // Construct this expected from a different expected type. -#define _CONVERTING_CTOR(UF, GF, ParamType, forward_func) \ - template && \ - expected_internal::convert_error_constraints)> \ - constexpr _EXPLICIT((!std::is_convertible_v || !std::is_convertible_v)) \ - expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v && \ - std::is_nothrow_constructible_v) \ - : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, forward_func(rhs.value())) \ - : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(const U&, const G&, const expected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(U, G, expected&&, std::move) - -#undef _CONVERTING_CTOR - - // Construct from (converted) success value, using a forwarding reference. - template , std::in_place_t> && - !std::is_same_v, expected> && - !expected_internal::is_unexpected_v> && - std::is_constructible_v && - (!std::is_same_v, bool> || - !expected_internal::is_expected_v>))> - constexpr _EXPLICIT((!std::is_convertible_v)) - // NOLINTNEXTLINE(google-explicit-constructor) - expected(U&& v) noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<0>, std::forward(v)) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) - - // in_place_t construction - template )> - constexpr explicit expected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<0>, std::forward(args)...) {} - - // in_place_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(std::in_place_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<0>, il, std::forward(args)...) {} - - // unexpect_t construction - template )> - constexpr explicit expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} - - // unexpect_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} - - // Assignment from (converted) success value, using a forwarding reference. - template > && - !expected_internal::is_unexpected_v> && - std::is_constructible_v && std::is_assignable_v && - expected_internal::eh_assign_constraints)> - constexpr expected& operator=(U&& v) noexcept(std::is_nothrow_constructible_v && - std::is_nothrow_assignable_v) { - if (has_value()) { - value() = std::forward(v); - } else { - var_.template emplace<0>(std::forward(v)); - } - return *this; - } - - _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , - (expected_internal::eh_assign_constraints)) - _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, - (expected_internal::eh_assign_constraints)) - - // modifiers - template )> - constexpr T& emplace(Args&&... args) noexcept { - var_.template emplace<0>(std::forward(args)...); - return value(); - } - - template &, Args...>)> - constexpr T& emplace(std::initializer_list il, Args&&... args) noexcept { - var_.template emplace<0>(il, std::forward(args)...); - return value(); - } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && - std::is_swappable_v && std::is_swappable_v && - std::is_move_constructible_v && std::is_move_constructible_v && - (std::is_nothrow_move_constructible_v || - std::is_nothrow_move_constructible_v))> - constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v && - std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v) { - var_.swap(rhs.var_); - } - - // observers - constexpr const T* operator->() const { return std::addressof(value()); } - constexpr T* operator->() { return std::addressof(value()); } - constexpr const T& operator*() const& { return value(); } - constexpr T& operator*() & { return value(); } - constexpr const T&& operator*() const&& { return std::move(std::get(var_)); } - constexpr T&& operator*() && { return std::move(std::get(var_)); } - - constexpr bool has_value() const noexcept { return var_.index() == 0; } - constexpr bool ok() const noexcept { return has_value(); } - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr const T& value() const& { return std::get(var_); } - constexpr T& value() & { return std::get(var_); } - constexpr const T&& value() const&& { return std::move(std::get(var_)); } - constexpr T&& value() && { return std::move(std::get(var_)); } - - constexpr const E& error() const& { return std::get(var_).error(); } - constexpr E& error() & { return std::get(var_).error(); } - constexpr const E&& error() const&& { return std::move(std::get(var_)).error(); } - constexpr E&& error() && { return std::move(std::get(var_)).error(); } - - template && - std::is_convertible_v - )> - constexpr T value_or(U&& v) const& { - if (has_value()) return value(); - else return static_cast(std::forward(v)); - } - - template && - std::is_convertible_v - )> - constexpr T value_or(U&& v) && { - if (has_value()) return std::move(value()); - else return static_cast(std::forward(v)); - } - - // expected equality operators - template - friend constexpr bool operator==(const expected& x, const expected& y); - template - friend constexpr bool operator!=(const expected& x, const expected& y); - - // Comparison with unexpected - template - friend constexpr bool operator==(const expected&, const unexpected&); - template - friend constexpr bool operator==(const unexpected&, const expected&); - template - friend constexpr bool operator!=(const expected&, const unexpected&); - template - friend constexpr bool operator!=(const unexpected&, const expected&); - - private: - using variant_type = std::variant; - variant_type var_; -}; - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return *x == *y; -} - -template -constexpr bool operator!=(const expected& x, const expected& y) { - return !(x == y); -} - -// Comparison with unexpected -template -constexpr bool operator==(const expected& x, const unexpected& y) { - return !x.has_value() && (x.error() == y.error()); -} -template -constexpr bool operator==(const unexpected& x, const expected& y) { - return !y.has_value() && (x.error() == y.error()); -} -template -constexpr bool operator!=(const expected& x, const unexpected& y) { - return x.has_value() || (x.error() != y.error()); -} -template -constexpr bool operator!=(const unexpected& x, const expected& y) { - return y.has_value() || (x.error() != y.error()); -} - -template -class _NODISCARD_ expected { - public: - using value_type = void; - using error_type = E; - using unexpected_type = unexpected; - - template - using rebind = expected; - - // Delegate simple operations to the underlying std::variant. - constexpr expected() = default; - constexpr expected(const expected& rhs) noexcept(std::is_nothrow_copy_constructible_v) = - default; - constexpr expected(expected&& rhs) noexcept(std::is_nothrow_move_constructible_v) = default; - constexpr expected& operator=(const expected& rhs) noexcept( - std::is_nothrow_copy_constructible_v && std::is_nothrow_copy_assignable_v) = default; - constexpr expected& operator=(expected&& rhs) noexcept( - std::is_nothrow_move_constructible_v && std::is_nothrow_move_assignable_v) = default; - - // Construct this expected from a different expected type. -#define _CONVERTING_CTOR(GF, ParamType, forward_func) \ - template && \ - expected_internal::convert_error_constraints)> \ - constexpr _EXPLICIT((!std::is_convertible_v)) \ - expected(ParamType rhs) noexcept(std::is_nothrow_constructible_v) \ - : var_(rhs.has_value() ? variant_type(std::in_place_index<0>, std::monostate()) \ - : variant_type(std::in_place_index<1>, forward_func(rhs.error()))) {} - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(const G&, const expected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONVERTING_CTOR(G, expected&&, std::move) - -#undef _CONVERTING_CTOR - - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(const G&, const unexpected&, ) - // NOLINTNEXTLINE(google-explicit-constructor) - _CONSTRUCT_EXPECTED_FROM_UNEXPECTED(G, unexpected&&, std::move) - - // in_place_t construction - constexpr explicit expected(std::in_place_t) noexcept {} - - // unexpect_t construction - template )> - constexpr explicit expected(unexpect_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : var_(std::in_place_index<1>, unexpected_type(std::forward(args)...)) {} - - // unexpect_t with initializer_list construction - template &, Args...>)> - constexpr explicit expected(unexpect_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : var_(std::in_place_index<1>, unexpected_type(il, std::forward(args)...)) {} - - _ASSIGN_UNEXPECTED_TO_EXPECTED(const G&, const unexpected&, , true) - _ASSIGN_UNEXPECTED_TO_EXPECTED(G, unexpected&&, std::move, true) - - // modifiers - constexpr void emplace() noexcept { var_.template emplace<0>(std::monostate()); } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && - std::is_swappable_v && std::is_move_constructible_v)> - constexpr void swap(expected& rhs) noexcept(std::is_nothrow_move_constructible_v && - std::is_nothrow_swappable_v) { - var_.swap(rhs.var_); - } - - // observers - constexpr bool has_value() const noexcept { return var_.index() == 0; } - constexpr bool ok() const noexcept { return has_value(); } - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr void value() const& { if (!has_value()) std::get<0>(var_); } - - constexpr const E& error() const& { return std::get<1>(var_).error(); } - constexpr E& error() & { return std::get<1>(var_).error(); } - constexpr const E&& error() const&& { return std::move(std::get<1>(var_)).error(); } - constexpr E&& error() && { return std::move(std::get<1>(var_)).error(); } - - // expected equality operators - template - friend constexpr bool operator==(const expected& x, const expected& y); - - private: - using variant_type = std::variant; - variant_type var_; -}; - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return true; -} - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return false; -} - -template -constexpr bool operator==(const expected& x, const expected& y) { - if (x.has_value() != y.has_value()) return false; - if (!x.has_value()) return x.error() == y.error(); - return false; -} - -template &>().swap(std::declval&>()))> -constexpr void swap(expected& x, expected& y) noexcept(noexcept(x.swap(y))) { - x.swap(y); -} - -template -class unexpected { - static_assert(std::is_object_v && !std::is_array_v && !std::is_const_v && - !std::is_volatile_v && !expected_internal::is_unexpected_v, - "unexpected error type cannot be a reference, a function, an array, cv-qualified, " - "or unexpected"); - - public: - // constructors - constexpr unexpected(const unexpected&) = default; - constexpr unexpected(unexpected&&) = default; - - template , unexpected> && - !std::is_same_v, std::in_place_t> && - std::is_constructible_v)> - constexpr explicit unexpected(Err&& e) noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(e)) {} - - template )> - constexpr explicit unexpected(std::in_place_t, Args&&... args) - noexcept(std::is_nothrow_constructible_v) - : val_(std::forward(args)...) {} - - template &, Args...>)> - constexpr explicit unexpected(std::in_place_t, std::initializer_list il, Args&&... args) - noexcept(std::is_nothrow_constructible_v&, Args...>) - : val_(il, std::forward(args)...) {} - - constexpr unexpected& operator=(const unexpected&) = default; - constexpr unexpected& operator=(unexpected&&) = default; - - // observer - constexpr const E& error() const& noexcept { return val_; } - constexpr E& error() & noexcept { return val_; } - constexpr const E&& error() const&& noexcept { return std::move(val_); } - constexpr E&& error() && noexcept { return std::move(val_); } - - // Swap. This function takes a template argument so that _ENABLE_IF works. - template && std::is_swappable_v)> - void swap(unexpected& other) noexcept(std::is_nothrow_swappable_v) { - // Make std::swap visible to provide swap for STL and builtin types, but use - // an unqualified swap to invoke argument-dependent lookup to find the swap - // functions for user-declared types. - using std::swap; - swap(val_, other.val_); - } - - template - friend constexpr bool - operator==(const unexpected& e1, const unexpected& e2); - template - friend constexpr bool - operator!=(const unexpected& e1, const unexpected& e2); - - private: - E val_; -}; - -template -constexpr bool -operator==(const unexpected& e1, const unexpected& e2) { - return e1.error() == e2.error(); -} - -template -constexpr bool -operator!=(const unexpected& e1, const unexpected& e2) { - return e1.error() != e2.error(); -} - -template )> -void swap(unexpected& x, unexpected& y) noexcept(noexcept(x.swap(y))) { - x.swap(y); -} - -// TODO: bad_expected_access class - -#undef _ENABLE_IF -#undef _NODISCARD_ -#undef _EXPLICIT -#undef _COMMA -#undef _CONSTRUCT_EXPECTED_FROM_UNEXPECTED -#undef _ASSIGN_UNEXPECTED_TO_EXPECTED - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/result.cpp b/sysbridge/src/main/cpp/android/libbase/result.cpp deleted file mode 100644 index c7163f270b..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/result.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "result.h" - -namespace android { -namespace base { - -ResultError MakeResultErrorWithCode(std::string&& message, Errno code) { - return ResultError(std::move(message) + ": " + code.print(), code); -} - -} // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/result.h b/sysbridge/src/main/cpp/android/libbase/result.h deleted file mode 100644 index 68c93344f8..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/result.h +++ /dev/null @@ -1,527 +0,0 @@ -/* - * Copyright (C) 2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// Result is the type that is used to pass a success value of type T or an error code of type -// E, optionally together with an error message. T and E can be any type. If E is omitted it -// defaults to int, which is useful when errno(3) is used as the error code. -// -// Passing a success value or an error value: -// -// Result readFile() { -// std::string content; -// if (base::ReadFileToString("path", &content)) { -// return content; // ok case -// } else { -// return ErrnoError() << "failed to read"; // error case -// } -// } -// -// Checking the result and then unwrapping the value or propagating the error: -// -// Result hasAWord() { -// auto content = readFile(); -// if (!content.ok()) { -// return Error() << "failed to process: " << content.error(); -// } -// return (*content.find("happy") != std::string::npos); -// } -// -// Using custom error code type: -// -// enum class MyError { A, B }; // assume that this is the error code you already have -// -// // To use the error code with Result, define a wrapper class that provides the following -// operations and use the wrapper class as the second type parameter (E) when instantiating -// Result -// -// 1. default constructor -// 2. copy constructor / and move constructor if copying is expensive -// 3. conversion operator to the error code type -// 4. value() function that return the error code value -// 5. print() function that gives a string representation of the error ode value -// -// struct MyErrorWrapper { -// MyError val_; -// MyErrorWrapper() : val_(/* reasonable default value */) {} -// MyErrorWrapper(MyError&& e) : val_(std:forward(e)) {} -// operator const MyError&() const { return val_; } -// MyError value() const { return val_; } -// std::string print() const { -// switch(val_) { -// MyError::A: return "A"; -// MyError::B: return "B"; -// } -// } -// }; -// -// #define NewMyError(e) Error(MyError::e) -// -// Result val = NewMyError(A) << "some message"; -// -// Formatting the error message using fmtlib: -// -// Errorf("{} errors", num); // equivalent to Error() << num << " errors"; -// ErrnoErrorf("{} errors", num); // equivalent to ErrnoError() << num << " errors"; -// -// Returning success or failure, but not the value: -// -// Result doSomething() { -// if (success) return {}; -// else return Error() << "error occurred"; -// } -// -// Extracting error code: -// -// Result val = Error(3) << "some error occurred"; -// assert(3 == val.error().code()); -// - -#pragma once - -#include -#include - -#include -#include -#include -#include -#include - -#include "errors.h" -#include "expected.h" - -namespace android { - namespace base { - -// Errno is a wrapper class for errno(3). Use this type instead of `int` when instantiating -// `Result` and `Error` template classes. This is required to distinguish errno from other -// integer-based error code types like `status_t`. - struct Errno { - Errno() : val_(0) {} - - Errno(int e) : val_(e) {} - - int value() const { return val_; } - - operator int() const { return value(); } - - const char *print() const { return strerror(value()); } - - int val_; - - // TODO(b/209929099): remove this conversion operator. This currently is needed to not break - // existing places where error().code() is used to construct enum values. - template>> - operator E() const { - return E(val_); - } - }; - - static_assert(std::is_trivially_copyable_v == true); - - template - struct ResultError { - template>> - ResultError(T &&message, P &&code) - : message_(std::forward(message)), code_(E(std::forward

(code))) {} - - ResultError(const ResultError &other) = default; - - ResultError(ResultError &&other) = default; - - ResultError &operator=(const ResultError &other) = default; - - ResultError &operator=(ResultError &&other) = default; - - template - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() &&{ - return android::base::unexpected(std::move(*this)); - } - - template - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const &{ - return android::base::unexpected(*this); - } - - const std::string &message() const { return message_; } - - const E &code() const { return code_; } - - private: - std::string message_; - E code_; - }; - - template - auto format_as(ResultError error) { - return error.message(); - } - - template - struct ResultError { - template>> - ResultError(P &&code) : code_(E(std::forward

(code))) {} - - template - operator android::base::expected>() const { - return android::base::unexpected(ResultError(code_)); - } - - const E &code() const { return code_; } - - private: - E code_; - }; - - template - inline bool operator==(const ResultError &lhs, const ResultError &rhs) { - return lhs.message() == rhs.message() && lhs.code() == rhs.code(); - } - - template - inline bool operator!=(const ResultError &lhs, const ResultError &rhs) { - return !(lhs == rhs); - } - - template - inline std::ostream &operator<<(std::ostream &os, const ResultError &t) { - os << t.message(); - return os; - } - - namespace internal { -// Stream class that does nothing and is has zero (actually 1) size. It is used instead of -// std::stringstream when include_message is false so that we use less on stack. -// sizeof(std::stringstream) is 280 on arm64. - struct DoNothingStream { - template - DoNothingStream &operator<<(T &&) { - return *this; - } - - std::string str() const { return ""; } - }; - } // namespace internal - - template>> - class Error { - public: - Error() : code_(0), has_code_(false) {} - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - Error(P &&code) : code_(std::forward

(code)), has_code_(true) {} - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const { - return android::base::unexpected(ResultError

(str(), static_cast

(code_))); - } - - template>> - // NOLINTNEXTLINE(google-explicit-constructor) - operator android::base::expected>() const { - return android::base::unexpected(ResultError(static_cast

(code_))); - } - - template - Error &operator<<(T &&t) { - static_assert(include_message, "<< not supported when include_message = false"); - // NOLINTNEXTLINE(bugprone-suspicious-semicolon) - if constexpr (std::is_same_v>, ResultError>) { - if (!has_code_) { - code_ = t.code(); - } - return (*this) << t.message(); - } - int saved = errno; - ss_ << t; - errno = saved; - return *this; - } - - const std::string str() const { - static_assert(include_message, "str() not supported when include_message = false"); - std::string str = ss_.str(); - if (has_code_) { - if (str.empty()) { - return code_.print(); - } - return std::move(str) + ": " + code_.print(); - } - return str; - } - - Error(const Error &) = delete; - - Error(Error &&) = delete; - - Error &operator=(const Error &) = delete; - - Error &operator=(Error &&) = delete; - - template - friend Error ErrorfImpl(const std::string &fmt, const Args &... args); - - template - friend Error ErrnoErrorfImpl(const std::string &fmt, const Args &... args); - - private: - Error(bool has_code, E code, const std::string &message) : code_(code), - has_code_(has_code) { - (*this) << message; - } - - std::conditional_t ss_; - E code_; - const bool has_code_; - }; - - inline Error ErrnoError() { - return Error(Errno{errno}); - } - - template - inline E ErrorCode(E code) { - return code; - } - -// Return the error code of the last ResultError object, if any. -// Otherwise, return `code` as it is. - template - inline E ErrorCode(E code, T &&t, const Args &... args) { - if constexpr (std::is_same_v>, ResultError>) { - return ErrorCode(t.code(), args...); - } - return ErrorCode(code, args...); - } - - __attribute__((noinline)) ResultError MakeResultErrorWithCode(std::string &&message, - Errno code); - - template - inline ResultError ErrorfImpl(const std::string &fmt, const Args &... args) { - return ResultError(fmt, ErrorCode(Errno{}, args...)); - } - - template - inline ResultError ErrnoErrorfImpl(const std::string &fmt, const Args &... args) { - Errno code{errno}; - return MakeResultErrorWithCode(std::string(fmt), code); - } - -#define Errorf(fmt, ...) android::base::ErrorfImpl(fmt, ##__VA_ARGS__) -#define ErrnoErrorf(fmt, ...) android::base::ErrnoErrorfImpl(fmt, ##__VA_ARGS__) - - template - using Result = android::base::expected>; - -// Specialization of android::base::OkOrFail for V = Result. See android-base/errors.h -// for the contract. - - namespace impl { - template - using Code = std::decay_t().error().code())>; - - template - using ErrorType = std::decay_t().error())>; - - template - constexpr bool IsNumeric = std::is_integral_v || std::is_floating_point_v || - (std::is_enum_v && std::is_convertible_v); - -// This base class exists to take advantage of shadowing -// We include the conversion in this base class so that if the conversion in NumericConversions -// overlaps, we (arbitrarily) choose the implementation in NumericConversions due to shadowing. - template - struct ConversionBase { - ErrorType error_; - - // T is a expected>. - operator T() const & { return unexpected(error_); } - - operator T() && { return unexpected(std::move(error_)); } - - operator Code() const { return error_.code(); } - }; - -// User defined conversions can be followed by numeric conversions -// Although we template specialize for the exact code type, we need -// specializations for conversions to all numeric types to avoid an -// ambiguous conversion sequence. - template - struct NumericConversions : public ConversionBase { - }; - - template - struct NumericConversions>> - > : public ConversionBase { -#pragma push_macro("SPECIALIZED_CONVERSION") -#define SPECIALIZED_CONVERSION(type) \ - operator expected>() const& { return unexpected(this->error_); } \ - operator expected>()&& { return unexpected(std::move(this->error_)); } - - SPECIALIZED_CONVERSION(int) - - SPECIALIZED_CONVERSION(short int) - - SPECIALIZED_CONVERSION(unsigned short int) - - SPECIALIZED_CONVERSION(unsigned int) - - SPECIALIZED_CONVERSION(long int) - - SPECIALIZED_CONVERSION(unsigned long int) - - SPECIALIZED_CONVERSION(long long int) - - SPECIALIZED_CONVERSION(unsigned long long int) - - SPECIALIZED_CONVERSION(bool) - - SPECIALIZED_CONVERSION(char) - - SPECIALIZED_CONVERSION(unsigned char) - - SPECIALIZED_CONVERSION(signed char) - - SPECIALIZED_CONVERSION(wchar_t) - - SPECIALIZED_CONVERSION(char16_t) - - SPECIALIZED_CONVERSION(char32_t) - - SPECIALIZED_CONVERSION(float) - - SPECIALIZED_CONVERSION(double) - - SPECIALIZED_CONVERSION(long double) - -#undef SPECIALIZED_CONVERSION -#pragma pop_macro("SPECIALIZED_CONVERSION") - // For debugging purposes - using IsNumericT = std::true_type; - }; - -#ifdef __cpp_concepts - template -// Define a concept which **any** type matches to - concept Universal = std::is_same_v; -#endif - -// A type that is never used. - struct Never { - }; - } // namespace impl - - template - struct OkOrFail> - : public impl::NumericConversions> { - using V = Result; - using Err = impl::ErrorType; - using C = impl::Code; - private: - OkOrFail(Err &&v) : impl::NumericConversions{std::move(v)} {} - - OkOrFail(const OkOrFail &other) = delete; - - OkOrFail(const OkOrFail &&other) = delete; - - public: - // Checks if V is ok or fail - static bool IsOk(const V &val) { return val.ok(); } - - // Turns V into a success value - static T Unwrap(V &&val) { - if constexpr (std::is_same_v) { - assert(IsOk(val)); - return; - } else { - return std::move(val.value()); - } - } - - // Consumes V when it's a fail value - static OkOrFail Fail(V &&v) { - assert(!IsOk(v)); - return OkOrFail{std::move(v.error())}; - } - - // We specialize as much as possible to avoid ambiguous conversion with templated expected ctor. - // We don't need this specialization if `C` is numeric because that case is already covered by - // `NumericConversions`. - operator Result, impl::Never, C>, E, include_message>() - const & { - return unexpected(this->error_); - } - - operator Result, impl::Never, C>, E, include_message>() && { - return unexpected(std::move(this->error_)); - } - -#ifdef __cpp_concepts - - // The idea here is to match this template method to any type (not simply trivial types). - // The reason for including a constraint is to take advantage of the fact that a constrained - // method always has strictly lower precedence than a non-constrained method in template - // specialization rules (thus avoiding ambiguity). So we use a universally matching constraint to - // mark this function as less preferable (but still accepting of all types). - template - operator Result() const &{ - return unexpected(this->error_); - } - - template - operator Result() &&{ - return unexpected(std::move(this->error_)); - } - -#else - template - operator Result() const& { - return unexpected(this->error_); - } - template - operator Result() && { - return unexpected(std::move(this->error_)); - } -#endif - - static const std::string &ErrorMessage(const V &val) { return val.error().message(); } - }; - -// Macros for testing the results of functions that return android::base::Result. These also work -// with base::android::expected. They assume the user depends on libgmock and includes -// gtest/gtest.h. For advanced matchers and customized error messages, see result-gmock.h. - -#define ASSERT_RESULT_OK(stmt) \ - if (const auto& tmp = (stmt); !tmp.ok()) \ - FAIL() << "Value of: " << #stmt << "\n" \ - << " Actual: " << tmp.error().message() << "\n" \ - << "Expected: is ok\n" - -#define EXPECT_RESULT_OK(stmt) \ - if (const auto& tmp = (stmt); !tmp.ok()) \ - ADD_FAILURE() << "Value of: " << #stmt << "\n" \ - << " Actual: " << tmp.error().message() << "\n" \ - << "Expected: is ok\n" - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp b/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp deleted file mode 100644 index d9eb0e501e..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/stringprintf.cpp +++ /dev/null @@ -1,85 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "stringprintf.h" - -#include - -#include - -namespace android { - namespace base { - - void StringAppendV(std::string *dst, const char *format, va_list ap) { - // First try with a small fixed size buffer - char space[1024] __attribute__((__uninitialized__)); - - // It's possible for methods that use a va_list to invalidate - // the data in it upon use. The fix is to make a copy - // of the structure before using it and use that copy instead. - va_list backup_ap; - va_copy(backup_ap, ap); - int result = vsnprintf(space, sizeof(space), format, backup_ap); - va_end(backup_ap); - - if (result < static_cast(sizeof(space))) { - if (result >= 0) { - // Normal case -- everything fit. - dst->append(space, result); - return; - } - - if (result < 0) { - // Just an error. - return; - } - } - - // Increase the buffer size to the size requested by vsnprintf, - // plus one for the closing \0. - int length = result + 1; - char *buf = new char[length]; - - // Restore the va_list before we use it again - va_copy(backup_ap, ap); - result = vsnprintf(buf, length, format, backup_ap); - va_end(backup_ap); - - if (result >= 0 && result < length) { - // It fit - dst->append(buf, result); - } - delete[] buf; - } - - std::string StringPrintf(const char *fmt, ...) { - va_list ap; - va_start(ap, fmt); - std::string result; - StringAppendV(&result, fmt, ap); - va_end(ap); - return result; - } - - void StringAppendF(std::string *dst, const char *format, ...) { - va_list ap; - va_start(ap, format); - StringAppendV(dst, format, ap); - va_end(ap); - } - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/libbase/stringprintf.h b/sysbridge/src/main/cpp/android/libbase/stringprintf.h deleted file mode 100644 index 02cc100d1a..0000000000 --- a/sysbridge/src/main/cpp/android/libbase/stringprintf.h +++ /dev/null @@ -1,41 +0,0 @@ -/* - * Copyright (C) 2011 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include - -namespace android { - namespace base { - -// These printf-like functions are implemented in terms of vsnprintf, so they -// use the same attribute for compile-time format string checking. - -// Returns a string corresponding to printf-like formatting of the arguments. - std::string - StringPrintf(const char *fmt, ...) __attribute__((__format__(__printf__, 1, 2))); - -// Appends a printf-like formatting of the arguments to 'dst'. - void StringAppendF(std::string *dst, const char *fmt, ...) - __attribute__((__format__(__printf__, 2, 3))); - -// Appends a printf-like formatting of the arguments to 'dst'. - void StringAppendV(std::string *dst, const char *format, va_list ap) - __attribute__((__format__(__printf__, 2, 0))); - - } // namespace base -} // namespace android diff --git a/sysbridge/src/main/cpp/android/liblog/log_main.h b/sysbridge/src/main/cpp/android/liblog/log_main.h deleted file mode 100644 index 8fc5ff4e79..0000000000 --- a/sysbridge/src/main/cpp/android/liblog/log_main.h +++ /dev/null @@ -1,369 +0,0 @@ -/* - * Copyright (C) 2005-2017 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - - -#include -#include - -#include - -__BEGIN_DECLS - -/* - * Normally we strip the effects of ALOGV (VERBOSE messages), - * LOG_FATAL and LOG_FATAL_IF (FATAL assert messages) from the - * release builds by defining NDEBUG. You can modify this (for - * example with "#define LOG_NDEBUG 0" at the top of your source - * file) to change that behavior. - */ - -#ifndef LOG_NDEBUG -#ifdef NDEBUG -#define LOG_NDEBUG 1 -#else -#define LOG_NDEBUG 0 -#endif -#endif - -/* --------------------------------------------------------------------- */ - -/* - * This file uses ", ## __VA_ARGS__" zero-argument token pasting to - * work around issues with debug-only syntax errors in assertions - * that are missing format strings. See commit - * 19299904343daf191267564fe32e6cd5c165cd42 - */ -#if defined(__clang__) -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wgnu-zero-variadic-macro-arguments" -#endif - -/* - * Use __VA_ARGS__ if running a static analyzer, - * to avoid warnings of unused variables in __VA_ARGS__. - * Use constexpr function in C++ mode, so these macros can be used - * in other constexpr functions without warning. - */ -#ifdef __clang_analyzer__ -#ifdef __cplusplus -extern "C++" { -template -constexpr int __fake_use_va_args(Ts...) { - return 0; -} -} -#else -extern int __fake_use_va_args(int, ...); -#endif /* __cplusplus */ -#define __FAKE_USE_VA_ARGS(...) ((void)__fake_use_va_args(0, ##__VA_ARGS__)) -#else -#define __FAKE_USE_VA_ARGS(...) ((void)(0)) -#endif /* __clang_analyzer__ */ - -#ifndef __predict_false -#define __predict_false(exp) __builtin_expect((exp) != 0, 0) -#endif - -#define android_writeLog(prio, tag, text) __android_log_write(prio, tag, text) - -#define android_printLog(prio, tag, ...) \ - __android_log_print(prio, tag, __VA_ARGS__) - -#define android_vprintLog(prio, cond, tag, ...) \ - __android_log_vprint(prio, tag, __VA_ARGS__) - -/* - * Log macro that allows you to specify a number for the priority. - */ -#ifndef LOG_PRI -#define LOG_PRI(priority, tag, ...) android_printLog(priority, tag, __VA_ARGS__) -#endif - -/* - * Log macro that allows you to pass in a varargs ("args" is a va_list). - */ -#ifndef LOG_PRI_VA -#define LOG_PRI_VA(priority, tag, fmt, args) \ - android_vprintLog(priority, NULL, tag, fmt, args) -#endif - -/* --------------------------------------------------------------------- */ - -/* XXX Macros to work around syntax errors in places where format string - * arg is not passed to ALOG_ASSERT, LOG_ALWAYS_FATAL or LOG_ALWAYS_FATAL_IF - * (happens only in debug builds). - */ - -/* Returns 2nd arg. Used to substitute default value if caller's vararg list - * is empty. - */ -#define __android_second(dummy, second, ...) second - -/* If passed multiple args, returns ',' followed by all but 1st arg, otherwise - * returns nothing. - */ -#define __android_rest(first, ...) , ##__VA_ARGS__ - -#define android_printAssert(cond, tag, ...) \ - __android_log_assert(cond, tag, \ - __android_second(0, ##__VA_ARGS__, NULL) \ - __android_rest(__VA_ARGS__)) - -/* - * Log a fatal error. If the given condition fails, this stops program - * execution like a normal assertion, but also generating the given message. - * It is NOT stripped from release builds. Note that the condition test - * is -inverted- from the normal assert() semantics. - */ -#ifndef LOG_ALWAYS_FATAL_IF -#define LOG_ALWAYS_FATAL_IF(cond, ...) \ - ((__predict_false(cond)) ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), \ - ((void)android_printAssert(#cond, LOG_TAG, ##__VA_ARGS__))) \ - : ((void)0)) -#endif - -#ifndef LOG_ALWAYS_FATAL -#define LOG_ALWAYS_FATAL(...) \ - (((void)android_printAssert(NULL, LOG_TAG, ##__VA_ARGS__))) -#endif - -/* - * Versions of LOG_ALWAYS_FATAL_IF and LOG_ALWAYS_FATAL that - * are stripped out of release builds. - */ - -#if LOG_NDEBUG - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#endif - -#else - -#ifndef LOG_FATAL_IF -#define LOG_FATAL_IF(cond, ...) LOG_ALWAYS_FATAL_IF(cond, ##__VA_ARGS__) -#endif -#ifndef LOG_FATAL -#define LOG_FATAL(...) LOG_ALWAYS_FATAL(__VA_ARGS__) -#endif - -#endif - -/* - * Assertion that generates a log message when the assertion fails. - * Stripped out of release builds. Uses the current LOG_TAG. - */ -#ifndef ALOG_ASSERT -#define ALOG_ASSERT(cond, ...) LOG_FATAL_IF(!(cond), ##__VA_ARGS__) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * C/C++ logging functions. See the logging documentation for API details. - * - * We'd like these to be available from C code (in case we import some from - * somewhere), so this has a C interface. - * - * The output will be correct when the log file is shared between multiple - * threads and/or multiple processes so long as the operating system - * supports O_APPEND. These calls have mutex-protected data structures - * and so are NOT reentrant. Do not use LOG in a signal handler. - */ - -/* --------------------------------------------------------------------- */ - -/* - * Simplified macro to send a verbose log message using the current LOG_TAG. - */ -#ifndef ALOGV -#define __ALOGV(...) ((void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) -#if LOG_NDEBUG -#define ALOGV(...) \ - do { \ - __FAKE_USE_VA_ARGS(__VA_ARGS__); \ - if (false) { \ - __ALOGV(__VA_ARGS__); \ - } \ - } while (false) -#else -#define ALOGV(...) __ALOGV(__VA_ARGS__) -#endif -#endif - -#ifndef ALOGV_IF -#if LOG_NDEBUG -#define ALOGV_IF(cond, ...) __FAKE_USE_VA_ARGS(__VA_ARGS__) -#else -#define ALOGV_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_VERBOSE, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif -#endif - -/* - * Simplified macro to send a debug log message using the current LOG_TAG. - */ -#ifndef ALOGD -#define ALOGD(...) ((void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGD_IF -#define ALOGD_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_DEBUG, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send an info log message using the current LOG_TAG. - */ -#ifndef ALOGI -#define ALOGI(...) ((void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGI_IF -#define ALOGI_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_INFO, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send a warning log message using the current LOG_TAG. - */ -#ifndef ALOGW -#define ALOGW(...) ((void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGW_IF -#define ALOGW_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_WARN, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* - * Simplified macro to send an error log message using the current LOG_TAG. - */ -#ifndef ALOGE -#define ALOGE(...) ((void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) -#endif - -#ifndef ALOGE_IF -#define ALOGE_IF(cond, ...) \ - ((__predict_false(cond)) \ - ? (__FAKE_USE_VA_ARGS(__VA_ARGS__), (void)ALOG(LOG_ERROR, LOG_TAG, __VA_ARGS__)) \ - : ((void)0)) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * verbose priority. - */ -#ifndef IF_ALOGV -#if LOG_NDEBUG -#define IF_ALOGV() if (false) -#else -#define IF_ALOGV() IF_ALOG(LOG_VERBOSE, LOG_TAG) -#endif -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * debug priority. - */ -#ifndef IF_ALOGD -#define IF_ALOGD() IF_ALOG(LOG_DEBUG, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * info priority. - */ -#ifndef IF_ALOGI -#define IF_ALOGI() IF_ALOG(LOG_INFO, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * warn priority. - */ -#ifndef IF_ALOGW -#define IF_ALOGW() IF_ALOG(LOG_WARN, LOG_TAG) -#endif - -/* - * Conditional based on whether the current LOG_TAG is enabled at - * error priority. - */ -#ifndef IF_ALOGE -#define IF_ALOGE() IF_ALOG(LOG_ERROR, LOG_TAG) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * Basic log message macro. - * - * Example: - * ALOG(LOG_WARN, NULL, "Failed with error %d", errno); - * - * The second argument may be NULL or "" to indicate the "global" tag. - */ -#ifndef ALOG -#define ALOG(priority, tag, ...) LOG_PRI(ANDROID_##priority, tag, __VA_ARGS__) -#endif - -/* - * Conditional given a desired logging priority and tag. - */ -#ifndef IF_ALOG -#define IF_ALOG(priority, tag) if (android_testLog(ANDROID_##priority, tag)) -#endif - -/* --------------------------------------------------------------------- */ - -/* - * IF_ALOG uses android_testLog, but IF_ALOG can be overridden. - * android_testLog will remain constant in its purpose as a wrapper - * for Android logging filter policy, and can be subject to - * change. It can be reused by the developers that override - * IF_ALOG as a convenient means to reimplement their policy - * over Android. - */ - -#if LOG_NDEBUG /* Production */ -#define android_testLog(prio, tag) \ - (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_DEBUG) != 0) -#else -#define android_testLog(prio, tag) \ - (__android_log_is_loggable_len(prio, tag, (tag) ? strlen(tag) : 0, ANDROID_LOG_VERBOSE) != 0) -#endif - -#if defined(__clang__) -#pragma clang diagnostic pop -#endif - -__END_DECLS diff --git a/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h b/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h deleted file mode 100644 index 6cb99396ad..0000000000 --- a/sysbridge/src/main/cpp/android/ui/LogicalDisplayId.h +++ /dev/null @@ -1,59 +0,0 @@ -/* - * Copyright 2024 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include - -#include - -namespace android::ui { - -// Type-safe wrapper for a logical display id. - struct LogicalDisplayId : ftl::Constructible, - ftl::Equatable, - ftl::Orderable { - using Constructible::Constructible; - - constexpr auto val() const { return ftl::to_underlying(*this); } - - constexpr bool isValid() const { return val() >= 0; } - - std::string toString() const { return std::to_string(val()); } - - static const LogicalDisplayId INVALID; - static const LogicalDisplayId DEFAULT; - }; - - constexpr inline LogicalDisplayId LogicalDisplayId::INVALID{-1}; - constexpr inline LogicalDisplayId LogicalDisplayId::DEFAULT{0}; - - inline std::ostream &operator<<(std::ostream &stream, LogicalDisplayId displayId) { - return stream << displayId.val(); - } - -} // namespace android::ui - -namespace std { - template<> - struct hash { - size_t operator()(const android::ui::LogicalDisplayId &displayId) const { - return hash()(displayId.val()); - } - }; -} // namespace std \ No newline at end of file diff --git a/sysbridge/src/main/cpp/android/utils/Errors.h b/sysbridge/src/main/cpp/android/utils/Errors.h deleted file mode 100644 index 22fb36d250..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Errors.h +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2007 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#pragma once - -#include -#include -#include -#include - -namespace android { - -/** - * The type used to return success/failure from frameworks APIs. - * See the anonymous enum below for valid values. - */ -typedef int32_t status_t; - -/* - * Error codes. - * All error codes are negative values. - */ - -enum { - OK = 0, // Preferred constant for checking success. -#ifndef NO_ERROR - // Win32 #defines NO_ERROR as well. It has the same value, so there's no - // real conflict, though it's a bit awkward. - NO_ERROR = OK, // Deprecated synonym for `OK`. Prefer `OK` because it doesn't conflict with Windows. -#endif - - UNKNOWN_ERROR = (-2147483647-1), // INT32_MIN value - - NO_MEMORY = -ENOMEM, - INVALID_OPERATION = -ENOSYS, - BAD_VALUE = -EINVAL, - BAD_TYPE = (UNKNOWN_ERROR + 1), - NAME_NOT_FOUND = -ENOENT, - PERMISSION_DENIED = -EPERM, - NO_INIT = -ENODEV, - ALREADY_EXISTS = -EEXIST, - DEAD_OBJECT = -EPIPE, - FAILED_TRANSACTION = (UNKNOWN_ERROR + 2), -#if !defined(_WIN32) - BAD_INDEX = -EOVERFLOW, - NOT_ENOUGH_DATA = -ENODATA, - WOULD_BLOCK = -EWOULDBLOCK, - TIMED_OUT = -ETIMEDOUT, - UNKNOWN_TRANSACTION = -EBADMSG, -#else - BAD_INDEX = -E2BIG, - NOT_ENOUGH_DATA = (UNKNOWN_ERROR + 3), - WOULD_BLOCK = (UNKNOWN_ERROR + 4), - TIMED_OUT = (UNKNOWN_ERROR + 5), - UNKNOWN_TRANSACTION = (UNKNOWN_ERROR + 6), -#endif - FDS_NOT_ALLOWED = (UNKNOWN_ERROR + 7), - UNEXPECTED_NULL = (UNKNOWN_ERROR + 8), -}; - -// Human readable name of error -std::string statusToString(status_t status); - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.cpp b/sysbridge/src/main/cpp/android/utils/FileMap.cpp deleted file mode 100644 index 21b7c65574..0000000000 --- a/sysbridge/src/main/cpp/android/utils/FileMap.cpp +++ /dev/null @@ -1,270 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Shared file mapping class. -// - -#define LOG_TAG "filemap" - -#include "FileMap.h" -#include "../../logging.h" -#include - -#if defined(__MINGW32__) && !defined(__USE_MINGW_ANSI_STDIO) -# define PRId32 "I32d" -# define PRIx32 "I32x" -# define PRId64 "I64d" -#else - -#include - -#endif - -#include -#include - -#if !defined(__MINGW32__) - -#include - -#endif - -#include -#include -#include -#include -#include - -using namespace android; - -/*static*/ long FileMap::mPageSize = -1; - -// Constructor. Create an empty object. -FileMap::FileMap(void) - : mFileName(nullptr), - mBasePtr(nullptr), - mBaseLength(0), - mDataPtr(nullptr), - mDataLength(0) -#if defined(__MINGW32__) -, - mFileHandle(INVALID_HANDLE_VALUE), - mFileMapping(NULL) -#endif -{ -} - -// Move Constructor. -FileMap::FileMap(FileMap &&other) noexcept - : mFileName(other.mFileName), - mBasePtr(other.mBasePtr), - mBaseLength(other.mBaseLength), - mDataOffset(other.mDataOffset), - mDataPtr(other.mDataPtr), - mDataLength(other.mDataLength) -#if defined(__MINGW32__) -, - mFileHandle(other.mFileHandle), - mFileMapping(other.mFileMapping) -#endif -{ - other.mFileName = nullptr; - other.mBasePtr = nullptr; - other.mDataPtr = nullptr; -#if defined(__MINGW32__) - other.mFileHandle = INVALID_HANDLE_VALUE; - other.mFileMapping = NULL; -#endif -} - -// Move assign operator. -FileMap &FileMap::operator=(FileMap &&other) noexcept { - mFileName = other.mFileName; - mBasePtr = other.mBasePtr; - mBaseLength = other.mBaseLength; - mDataOffset = other.mDataOffset; - mDataPtr = other.mDataPtr; - mDataLength = other.mDataLength; - other.mFileName = nullptr; - other.mBasePtr = nullptr; - other.mDataPtr = nullptr; -#if defined(__MINGW32__) - mFileHandle = other.mFileHandle; - mFileMapping = other.mFileMapping; - other.mFileHandle = INVALID_HANDLE_VALUE; - other.mFileMapping = NULL; -#endif - return *this; -} - -// Destructor. -FileMap::~FileMap(void) { - if (mFileName != nullptr) { - free(mFileName); - } -#if defined(__MINGW32__) - if (mBasePtr && UnmapViewOfFile(mBasePtr) == 0) { - LOGD("UnmapViewOfFile(%p) failed, error = %lu\n", mBasePtr, - GetLastError() ); - } - if (mFileMapping != NULL) { - CloseHandle(mFileMapping); - } -#else - if (mBasePtr && munmap(mBasePtr, mBaseLength) != 0) { - LOGD("munmap(%p, %zu) failed\n", mBasePtr, mBaseLength); - } -#endif -} - - -// Create a new mapping on an open file. -// -// Closing the file descriptor does not unmap the pages, so we don't -// claim ownership of the fd. -// -// Returns "false" on failure. -bool FileMap::create(const char *origFileName, int fd, off64_t offset, size_t length, - bool readOnly) { -#if defined(__MINGW32__) - int adjust; - off64_t adjOffset; - size_t adjLength; - - if (mPageSize == -1) { - SYSTEM_INFO si; - - GetSystemInfo( &si ); - mPageSize = si.dwAllocationGranularity; - } - - DWORD protect = readOnly ? PAGE_READONLY : PAGE_READWRITE; - - mFileHandle = (HANDLE) _get_osfhandle(fd); - mFileMapping = CreateFileMapping( mFileHandle, NULL, protect, 0, 0, NULL); - if (mFileMapping == NULL) { - LOGE("CreateFileMapping(%p, %lx) failed with error %lu\n", - mFileHandle, protect, GetLastError() ); - return false; - } - - adjust = offset % mPageSize; - adjOffset = offset - adjust; - adjLength = length + adjust; - - mBasePtr = MapViewOfFile( mFileMapping, - readOnly ? FILE_MAP_READ : FILE_MAP_ALL_ACCESS, - 0, - (DWORD)(adjOffset), - adjLength ); - if (mBasePtr == NULL) { - LOGE("MapViewOfFile(%" PRId64 ", %zu) failed with error %lu\n", - adjOffset, adjLength, GetLastError() ); - CloseHandle(mFileMapping); - mFileMapping = NULL; - return false; - } -#else // !defined(__MINGW32__) - assert(fd >= 0); - assert(offset >= 0); - assert(length > 0); - - // init on first use - if (mPageSize == -1) { - mPageSize = sysconf(_SC_PAGESIZE); - if (mPageSize == -1) { - LOGE("could not get _SC_PAGESIZE\n"); - return false; - } - } - - int adjust = offset % mPageSize; - off64_t adjOffset = offset - adjust; - size_t adjLength; - if (__builtin_add_overflow(length, adjust, &adjLength)) { - LOGE("adjusted length overflow: length %zu adjust %d", length, adjust); - return false; - } - - int flags = MAP_SHARED; - int prot = PROT_READ; - if (!readOnly) prot |= PROT_WRITE; - - void *ptr = mmap64(nullptr, adjLength, prot, flags, fd, adjOffset); - if (ptr == MAP_FAILED) { - if (errno == EINVAL && length == 0) { - ptr = nullptr; - adjust = 0; - } else { - LOGE("mmap(%lld,%zu) failed: %s\n", (long long) adjOffset, adjLength, strerror(errno)); - return false; - } - } - mBasePtr = ptr; -#endif // !defined(__MINGW32__) - - mFileName = origFileName != nullptr ? strdup(origFileName) : nullptr; - mBaseLength = adjLength; - mDataOffset = offset; - mDataPtr = (char *) mBasePtr + adjust; - mDataLength = length; - - LOGV("MAP: base %p/%zu data %p/%zu\n", - mBasePtr, mBaseLength, mDataPtr, mDataLength); - - return true; -} - -// Provide guidance to the system. -#if !defined(_WIN32) - -int FileMap::advise(MapAdvice advice) { - int cc, sysAdvice; - - switch (advice) { - case NORMAL: - sysAdvice = MADV_NORMAL; - break; - case RANDOM: - sysAdvice = MADV_RANDOM; - break; - case SEQUENTIAL: - sysAdvice = MADV_SEQUENTIAL; - break; - case WILLNEED: - sysAdvice = MADV_WILLNEED; - break; - case DONTNEED: - sysAdvice = MADV_DONTNEED; - break; - default: - assert(false); - return -1; - } - - cc = madvise(mBasePtr, mBaseLength, sysAdvice); - if (cc != 0) - LOGW("madvise(%d) failed: %s\n", sysAdvice, strerror(errno)); - return cc; -} - -#else -int FileMap::advise(MapAdvice /* advice */) -{ - return -1; -} -#endif diff --git a/sysbridge/src/main/cpp/android/utils/FileMap.h b/sysbridge/src/main/cpp/android/utils/FileMap.h deleted file mode 100644 index 4b37a5f7f4..0000000000 --- a/sysbridge/src/main/cpp/android/utils/FileMap.h +++ /dev/null @@ -1,129 +0,0 @@ -/* - * Copyright (C) 2006 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -// -// Encapsulate a shared file mapping. -// -#ifndef __LIBS_FILE_MAP_H -#define __LIBS_FILE_MAP_H - -#include - -#if defined(__MINGW32__) -// Ensure that we always pull in winsock2.h before windows.h -#if defined(_WIN32) -#include -#endif -#include -#endif - -namespace android { - -/* - * This represents a memory-mapped file. It might be the entire file or - * only part of it. This requires a little bookkeeping because the mapping - * needs to be aligned on page boundaries, and in some cases we'd like to - * have multiple references to the mapped area without creating additional - * maps. - * - * This always uses MAP_SHARED. - * - * TODO: we should be able to create a new FileMap that is a subset of - * an existing FileMap and shares the underlying mapped pages. Requires - * completing the refcounting stuff and possibly introducing the notion - * of a FileMap hierarchy. - */ - class FileMap { - public: - FileMap(void); - - FileMap(FileMap &&f) noexcept; - - FileMap &operator=(FileMap &&f) noexcept; - - /* - * Create a new mapping on an open file. - * - * Closing the file descriptor does not unmap the pages, so we don't - * claim ownership of the fd. - * - * Returns "false" on failure. - */ - bool create(const char *origFileName, int fd, - off64_t offset, size_t length, bool readOnly); - - ~FileMap(void); - - /* - * Return the name of the file this map came from, if known. - */ - const char *getFileName(void) const { return mFileName; } - - /* - * Get a pointer to the piece of the file we requested. - */ - void *getDataPtr(void) const { return mDataPtr; } - - /* - * Get the length we requested. - */ - size_t getDataLength(void) const { return mDataLength; } - - /* - * Get the data offset used to create this map. - */ - off64_t getDataOffset(void) const { return mDataOffset; } - - /* - * This maps directly to madvise() values, but allows us to avoid - * including everywhere. - */ - enum MapAdvice { - NORMAL, RANDOM, SEQUENTIAL, WILLNEED, DONTNEED - }; - - /* - * Apply an madvise() call to the entire file. - * - * Returns 0 on success, -1 on failure. - */ - int advise(MapAdvice advice); - - protected: - - private: - // these are not implemented - FileMap(const FileMap &src); - - const FileMap &operator=(const FileMap &src); - - char *mFileName; // original file name, if known - void *mBasePtr; // base of mmap area; page aligned - size_t mBaseLength; // length, measured from "mBasePtr" - off64_t mDataOffset; // offset used when map was created - void *mDataPtr; // start of requested data, offset from base - size_t mDataLength; // length, measured from "mDataPtr" -#if defined(__MINGW32__) - HANDLE mFileHandle; // Win32 file handle - HANDLE mFileMapping; // Win32 file mapping handle -#endif - - static long mPageSize; - }; - -} // namespace android - -#endif // __LIBS_FILE_MAP_H diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp b/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp deleted file mode 100644 index 8ee62e3bb3..0000000000 --- a/sysbridge/src/main/cpp/android/utils/SharedBuffer.cpp +++ /dev/null @@ -1,122 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "sharedbuffer" - -#include "SharedBuffer.h" - -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - - SharedBuffer *SharedBuffer::alloc(size_t size) { - SharedBuffer *sb = static_cast(malloc(sizeof(SharedBuffer) + size)); - if (sb) { - // Should be std::atomic_init(&sb->mRefs, 1); - // But that generates a warning with some compilers. - // The following is OK on Android-supported platforms. - sb->mRefs.store(1, std::memory_order_relaxed); - sb->mSize = size; - sb->mClientMetadata = 0; - } - return sb; - } - - - void SharedBuffer::dealloc(const SharedBuffer *released) { - free(const_cast(released)); - } - - SharedBuffer *SharedBuffer::edit() const { - if (onlyOwner()) { - return const_cast(this); - } - SharedBuffer *sb = alloc(mSize); - if (sb) { - memcpy(sb->data(), data(), size()); - release(); - } - return sb; - } - - SharedBuffer *SharedBuffer::editResize(size_t newSize) const { - if (onlyOwner()) { - SharedBuffer *buf = const_cast(this); - if (buf->mSize == newSize) return buf; - buf = (SharedBuffer *) realloc(reinterpret_cast(buf), - sizeof(SharedBuffer) + newSize); - if (buf != nullptr) { - buf->mSize = newSize; - return buf; - } - } - SharedBuffer *sb = alloc(newSize); - if (sb) { - const size_t mySize = mSize; - memcpy(sb->data(), data(), newSize < mySize ? newSize : mySize); - release(); - } - return sb; - } - - SharedBuffer *SharedBuffer::attemptEdit() const { - if (onlyOwner()) { - return const_cast(this); - } - return nullptr; - } - - SharedBuffer *SharedBuffer::reset(size_t new_size) const { - // cheap-o-reset. - SharedBuffer *sb = alloc(new_size); - if (sb) { - release(); - } - return sb; - } - - void SharedBuffer::acquire() const { - mRefs.fetch_add(1, std::memory_order_relaxed); - } - - int32_t SharedBuffer::release(uint32_t flags) const { - const bool useDealloc = ((flags & eKeepStorage) == 0); - if (onlyOwner()) { - // Since we're the only owner, our reference count goes to zero. - mRefs.store(0, std::memory_order_relaxed); - if (useDealloc) { - dealloc(this); - } - // As the only owner, our previous reference count was 1. - return 1; - } - // There's multiple owners, we need to use an atomic decrement. - int32_t prevRefCount = mRefs.fetch_sub(1, std::memory_order_release); - if (prevRefCount == 1) { - // We're the last reference, we need the acquire fence. - atomic_thread_fence(std::memory_order_acquire); - if (useDealloc) { - dealloc(this); - } - } - return prevRefCount; - } - - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/SharedBuffer.h b/sysbridge/src/main/cpp/android/utils/SharedBuffer.h deleted file mode 100644 index c170df00e1..0000000000 --- a/sysbridge/src/main/cpp/android/utils/SharedBuffer.h +++ /dev/null @@ -1,153 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -/* - * DEPRECATED. DO NOT USE FOR NEW CODE. - */ - -#ifndef ANDROID_SHARED_BUFFER_H -#define ANDROID_SHARED_BUFFER_H - -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - - class SharedBuffer { - public: - - /* flags to use with release() */ - enum { - eKeepStorage = 0x00000001 - }; - - /*! allocate a buffer of size 'size' and acquire() it. - * call release() to free it. - */ - static SharedBuffer *alloc(size_t size); - - /*! free the memory associated with the SharedBuffer. - * Fails if there are any users associated with this SharedBuffer. - * In other words, the buffer must have been release by all its - * users. - */ - static void dealloc(const SharedBuffer *released); - - //! access the data for read - inline const void *data() const; - - //! access the data for read/write - inline void *data(); - - //! get size of the buffer - inline size_t size() const; - - //! get back a SharedBuffer object from its data - static inline SharedBuffer *bufferFromData(void *data); - - //! get back a SharedBuffer object from its data - static inline const SharedBuffer *bufferFromData(const void *data); - - //! get the size of a SharedBuffer object from its data - static inline size_t sizeFromData(const void *data); - - //! edit the buffer (get a writtable, or non-const, version of it) - SharedBuffer *edit() const; - - //! edit the buffer, resizing if needed - SharedBuffer *editResize(size_t size) const; - - //! like edit() but fails if a copy is required - SharedBuffer *attemptEdit() const; - - //! resize and edit the buffer, loose it's content. - SharedBuffer *reset(size_t size) const; - - //! acquire/release a reference on this buffer - void acquire() const; - - /*! release a reference on this buffer, with the option of not - * freeing the memory associated with it if it was the last reference - * returns the previous reference count - */ - int32_t release(uint32_t flags = 0) const; - - //! returns wether or not we're the only owner - inline bool onlyOwner() const; - - - private: - inline SharedBuffer() {} - - inline ~SharedBuffer() {} - - SharedBuffer(const SharedBuffer &); - - SharedBuffer &operator=(const SharedBuffer &); - - // Must be sized to preserve correct alignment. - mutable std::atomic mRefs; - size_t mSize; - uint32_t mReserved; - public: - // mClientMetadata is reserved for client use. It is initialized to 0 - // and the clients can do whatever they want with it. Note that this is - // placed last so that it is adjcent to the buffer allocated. - uint32_t mClientMetadata; - }; - - static_assert(sizeof(SharedBuffer) % 8 == 0 - && (sizeof(size_t) > 4 || sizeof(SharedBuffer) == 16), - "SharedBuffer has unexpected size"); - -// --------------------------------------------------------------------------- - - const void *SharedBuffer::data() const { - return this + 1; - } - - void *SharedBuffer::data() { - return this + 1; - } - - size_t SharedBuffer::size() const { - return mSize; - } - - SharedBuffer *SharedBuffer::bufferFromData(void *data) { - return data ? static_cast(data) - 1 : nullptr; - } - - const SharedBuffer *SharedBuffer::bufferFromData(const void *data) { - return data ? static_cast(data) - 1 : nullptr; - } - - size_t SharedBuffer::sizeFromData(const void *data) { - return data ? bufferFromData(data)->mSize : 0; - } - - bool SharedBuffer::onlyOwner() const { - return (mRefs.load(std::memory_order_acquire) == 1); - } - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_VECTOR_H diff --git a/sysbridge/src/main/cpp/android/utils/String16.cpp b/sysbridge/src/main/cpp/android/utils/String16.cpp deleted file mode 100644 index 0d61425d7c..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String16.cpp +++ /dev/null @@ -1,393 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String16.h" - -#include -#include - -#include "SharedBuffer.h" - -#define LIBUTILS_PRAGMA(arg) _Pragma(#arg) -#if defined(__clang__) -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(clang arg) -#elif defined(__GNUC__) -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) LIBUTILS_PRAGMA(GCC arg) -#else -#define LIBUTILS_PRAGMA_FOR_COMPILER(arg) -#endif -#define LIBUTILS_IGNORE(warning_flag) \ - LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic push) \ - LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic ignored warning_flag) -#define LIBUTILS_IGNORE_END() LIBUTILS_PRAGMA_FOR_COMPILER(diagnostic pop) - -namespace android { - -static const StaticString16 emptyString(u""); -static inline char16_t* getEmptyString() { - return const_cast(emptyString.c_str()); -} - -// --------------------------------------------------------------------------- - -void* String16::alloc(size_t size) -{ - SharedBuffer* buf = SharedBuffer::alloc(size); - buf->mClientMetadata = kIsSharedBufferAllocated; - return buf; -} - -char16_t* String16::allocFromUTF8(const char* u8str, size_t u8len) -{ - if (u8len == 0) return getEmptyString(); - - const uint8_t* u8cur = (const uint8_t*) u8str; - - const ssize_t u16len = utf8_to_utf16_length(u8cur, u8len); - if (u16len < 0) { - return getEmptyString(); - } - - SharedBuffer* buf = static_cast(alloc(sizeof(char16_t) * (u16len + 1))); - if (buf) { - u8cur = (const uint8_t*) u8str; - char16_t* u16str = (char16_t*)buf->data(); - - utf8_to_utf16(u8cur, u8len, u16str, ((size_t) u16len) + 1); - - //printf("Created UTF-16 string from UTF-8 \"%s\":", in); - //printHexData(1, str, buf->size(), 16, 1); - //printf("\n"); - - return u16str; - } - - return getEmptyString(); -} - -char16_t* String16::allocFromUTF16(const char16_t* u16str, size_t u16len) { - if (u16len >= SIZE_MAX / sizeof(char16_t)) { - abort(); - } - - SharedBuffer* buf = static_cast(alloc((u16len + 1) * sizeof(char16_t))); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memcpy(str, u16str, u16len * sizeof(char16_t)); - str[u16len] = 0; - return str; - } - return getEmptyString(); -} - -// --------------------------------------------------------------------------- - -String16::String16() - : mString(getEmptyString()) -{ -} - -String16::String16(const String16& o) - : mString(o.mString) -{ - acquire(); -} - -String16::String16(String16&& o) noexcept - : mString(o.mString) -{ - o.mString = getEmptyString(); -} - -String16::String16(const String16& o, size_t len, size_t begin) - : mString(getEmptyString()) -{ - setTo(o, len, begin); -} - -String16::String16(const char16_t* o) : mString(allocFromUTF16(o, strlen16(o))) {} - -String16::String16(const char16_t* o, size_t len) : mString(allocFromUTF16(o, len)) {} - -String16::String16(const String8& o) : mString(allocFromUTF8(o.c_str(), o.size())) {} - -String16::String16(const char* o) - : mString(allocFromUTF8(o, strlen(o))) -{ -} - -String16::String16(const char* o, size_t len) - : mString(allocFromUTF8(o, len)) -{ -} - -String16::~String16() -{ - release(); -} - -String16& String16::operator=(String16&& other) noexcept { - release(); - mString = other.mString; - other.mString = getEmptyString(); - return *this; -} - -size_t String16::size() const -{ - if (isStaticString()) { - return staticStringSize(); - } else { - return SharedBuffer::sizeFromData(mString) / sizeof(char16_t) - 1; - } -} - -void String16::setTo(const String16& other) -{ - release(); - mString = other.mString; - acquire(); -} - -status_t String16::setTo(const String16& other, size_t len, size_t begin) -{ - const size_t N = other.size(); - if (begin >= N) { - release(); - mString = getEmptyString(); - return OK; - } - if ((begin+len) > N) len = N-begin; - if (begin == 0 && len == N) { - setTo(other); - return OK; - } - - return setTo(other.c_str() + begin, len); -} - -status_t String16::setTo(const char16_t* other) -{ - return setTo(other, strlen16(other)); -} - -status_t String16::setTo(const char16_t* other, size_t len) -{ - if (len >= SIZE_MAX / sizeof(char16_t)) { - abort(); - } - - SharedBuffer* buf = static_cast(editResize((len + 1) * sizeof(char16_t))); - if (buf) { - char16_t* str = (char16_t*)buf->data(); - memmove(str, other, len*sizeof(char16_t)); - str[len] = 0; - mString = str; - return OK; - } - return NO_MEMORY; -} - -status_t String16::append(const String16& other) { - return append(other.c_str(), other.size()); -} - -status_t String16::append(const char16_t* chrs, size_t otherLen) { - const size_t myLen = size(); - - if (myLen == 0) return setTo(chrs, otherLen); - - if (otherLen == 0) return OK; - - size_t size = myLen; - if (__builtin_add_overflow(size, otherLen, &size) || - __builtin_add_overflow(size, 1, &size) || - __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY; - - SharedBuffer* buf = static_cast(editResize(size)); - if (!buf) return NO_MEMORY; - - char16_t* str = static_cast(buf->data()); - memcpy(str + myLen, chrs, otherLen * sizeof(char16_t)); - str[myLen + otherLen] = 0; - mString = str; - return OK; -} - -status_t String16::insert(size_t pos, const char16_t* chrs) { - return insert(pos, chrs, strlen16(chrs)); -} - -status_t String16::insert(size_t pos, const char16_t* chrs, size_t otherLen) { - const size_t myLen = size(); - - if (myLen == 0) return setTo(chrs, otherLen); - - if (otherLen == 0) return OK; - - if (pos > myLen) pos = myLen; - - size_t size = myLen; - if (__builtin_add_overflow(size, otherLen, &size) || - __builtin_add_overflow(size, 1, &size) || - __builtin_mul_overflow(size, sizeof(char16_t), &size)) return NO_MEMORY; - - SharedBuffer* buf = static_cast(editResize(size)); - if (!buf) return NO_MEMORY; - - char16_t* str = static_cast(buf->data()); - if (pos < myLen) memmove(str + pos + otherLen, str + pos, (myLen - pos) * sizeof(char16_t)); - memcpy(str + pos, chrs, otherLen * sizeof(char16_t)); - str[myLen + otherLen] = 0; - mString = str; - return OK; -} - -ssize_t String16::findFirst(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - if (*p == c) { - return p-str; - } - p++; - } - return -1; -} - -ssize_t String16::findLast(char16_t c) const -{ - const char16_t* str = string(); - const char16_t* p = str; - const char16_t* e = p + size(); - while (p < e) { - e--; - if (*e == c) { - return e-str; - } - } - return -1; -} - -bool String16::startsWith(const String16& prefix) const -{ - const size_t ps = prefix.size(); - if (ps > size()) return false; - return strzcmp16(mString, ps, prefix.c_str(), ps) == 0; -} - -bool String16::startsWith(const char16_t* prefix) const -{ - const size_t ps = strlen16(prefix); - if (ps > size()) return false; - return strncmp16(mString, prefix, ps) == 0; -} - -bool String16::contains(const char16_t* chrs) const -{ - return strstr16(mString, chrs) != nullptr; -} - -void* String16::edit() { - SharedBuffer* buf; - if (isStaticString()) { - buf = static_cast(alloc((size() + 1) * sizeof(char16_t))); - if (buf) { - memcpy(buf->data(), mString, (size() + 1) * sizeof(char16_t)); - } - } else { - buf = SharedBuffer::bufferFromData(mString)->edit(); - buf->mClientMetadata = kIsSharedBufferAllocated; - } - return buf; -} - -void* String16::editResize(size_t newSize) { - SharedBuffer* buf; - if (isStaticString()) { - size_t copySize = (size() + 1) * sizeof(char16_t); - if (newSize < copySize) { - copySize = newSize; - } - buf = static_cast(alloc(newSize)); - if (buf) { - memcpy(buf->data(), mString, copySize); - } - } else { - buf = SharedBuffer::bufferFromData(mString)->editResize(newSize); - buf->mClientMetadata = kIsSharedBufferAllocated; - } - return buf; -} - -void String16::acquire() -{ - if (!isStaticString()) { - SharedBuffer::bufferFromData(mString)->acquire(); - } -} - -void String16::release() -{ - if (!isStaticString()) { - SharedBuffer::bufferFromData(mString)->release(); - } -} - -bool String16::isStaticString() const { - // See String16.h for notes on the memory layout of String16::StaticData and - // SharedBuffer. - LIBUTILS_IGNORE("-Winvalid-offsetof") - static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4); - LIBUTILS_IGNORE_END() - const uint32_t* p = reinterpret_cast(mString); - return (*(p - 1) & kIsSharedBufferAllocated) == 0; -} - -size_t String16::staticStringSize() const { - // See String16.h for notes on the memory layout of String16::StaticData and - // SharedBuffer. - LIBUTILS_IGNORE("-Winvalid-offsetof") - static_assert(sizeof(SharedBuffer) - offsetof(SharedBuffer, mClientMetadata) == 4); - LIBUTILS_IGNORE_END() - const uint32_t* p = reinterpret_cast(mString); - return static_cast(*(p - 1)); -} - -status_t String16::replaceAll(char16_t replaceThis, char16_t withThis) -{ - const size_t N = size(); - const char16_t* str = string(); - char16_t* edited = nullptr; - for (size_t i=0; i(edit()); - if (!buf) { - return NO_MEMORY; - } - edited = (char16_t*)buf->data(); - mString = str = edited; - } - edited[i] = withThis; - } - } - return OK; -} - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/String16.h b/sysbridge/src/main/cpp/android/utils/String16.h deleted file mode 100644 index 54c6d21ae6..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String16.h +++ /dev/null @@ -1,411 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING16_H -#define ANDROID_STRING16_H - -#include -#include -#include - -#include "Errors.h" -#include "String8.h" -#include "TypeHelpers.h" - -#if __cplusplus >= 202002L -#include -#endif - -// --------------------------------------------------------------------------- - -namespace android { - -// --------------------------------------------------------------------------- - -template -class StaticString16; - -// DO NOT USE: please use std::u16string - -//! This is a string holding UTF-16 characters. -class String16 -{ -public: - String16(); - String16(const String16& o); - String16(String16&& o) noexcept; - String16(const String16& o, - size_t len, - size_t begin=0); - explicit String16(const char16_t* o); - explicit String16(const char16_t* o, size_t len); - explicit String16(const String8& o); - explicit String16(const char* o); - explicit String16(const char* o, size_t len); - - ~String16(); - - inline const char16_t* c_str() const; - - size_t size() const; - inline bool empty() const; - - inline size_t length() const; - - void setTo(const String16& other); - status_t setTo(const char16_t* other); - status_t setTo(const char16_t* other, size_t len); - status_t setTo(const String16& other, - size_t len, - size_t begin=0); - - status_t append(const String16& other); - status_t append(const char16_t* other, size_t len); - - inline String16& operator=(const String16& other); - String16& operator=(String16&& other) noexcept; - - inline String16& operator+=(const String16& other); - inline String16 operator+(const String16& other) const; - - status_t insert(size_t pos, const char16_t* chrs); - status_t insert(size_t pos, - const char16_t* chrs, size_t len); - - ssize_t findFirst(char16_t c) const; - ssize_t findLast(char16_t c) const; - - bool startsWith(const String16& prefix) const; - bool startsWith(const char16_t* prefix) const; - - bool contains(const char16_t* chrs) const; - inline bool contains(const String16& other) const; - - status_t replaceAll(char16_t replaceThis, - char16_t withThis); - - inline int compare(const String16& other) const; - - inline bool operator<(const String16& other) const; - inline bool operator<=(const String16& other) const; - inline bool operator==(const String16& other) const; - inline bool operator!=(const String16& other) const; - inline bool operator>=(const String16& other) const; - inline bool operator>(const String16& other) const; -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const String16& other) const; -#endif - - inline bool operator<(const char16_t* other) const; - inline bool operator<=(const char16_t* other) const; - inline bool operator==(const char16_t* other) const; - inline bool operator!=(const char16_t* other) const; - inline bool operator>=(const char16_t* other) const; - inline bool operator>(const char16_t* other) const; -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const char16_t* other) const; -#endif - - inline operator const char16_t*() const; - - // Implicit cast to std::u16string is not implemented on purpose - u16string_view is much - // lighter and if one needs, they can still create u16string from u16string_view. - inline operator std::u16string_view() const; - - // Static and non-static String16 behave the same for the users, so - // this method isn't of much use for the users. It is public for testing. - bool isStaticString() const; - - private: - /* - * A flag indicating the type of underlying buffer. - */ - static constexpr uint32_t kIsSharedBufferAllocated = 0x80000000; - - /* - * alloc() returns void* so that SharedBuffer class is not exposed. - */ - static void* alloc(size_t size); - static char16_t* allocFromUTF8(const char* u8str, size_t u8len); - static char16_t* allocFromUTF16(const char16_t* u16str, size_t u16len); - - /* - * edit() and editResize() return void* so that SharedBuffer class - * is not exposed. - */ - void* edit(); - void* editResize(size_t new_size); - - void acquire(); - void release(); - - size_t staticStringSize() const; - - const char16_t* mString; - -protected: - /* - * Data structure used to allocate static storage for static String16. - * - * Note that this data structure and SharedBuffer are used interchangably - * as the underlying data structure for a String16. Therefore, the layout - * of this data structure must match the part in SharedBuffer that is - * visible to String16. - */ - template - struct StaticData { - // The high bit of 'size' is used as a flag. - static_assert(N - 1 < kIsSharedBufferAllocated, "StaticString16 too long!"); - constexpr StaticData() : size(N - 1), data{0} {} - const uint32_t size; - char16_t data[N]; - - constexpr StaticData(const StaticData&) = default; - }; - - /* - * Helper function for constructing a StaticData object. - */ - template - static constexpr const StaticData makeStaticData(const char16_t (&s)[N]) { - StaticData r; - // The 'size' field is at the same location where mClientMetadata would - // be for a SharedBuffer. We do NOT set kIsSharedBufferAllocated flag - // here. - for (size_t i = 0; i < N - 1; ++i) r.data[i] = s[i]; - return r; - } - - template - explicit constexpr String16(const StaticData& s) : mString(s.data) {} - -// These symbols are for potential backward compatibility with prebuilts. To be removed. -#ifdef ENABLE_STRING16_OBSOLETE_METHODS -public: -#else -private: -#endif - inline const char16_t* string() const; -}; - -// String16 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. -ANDROID_TRIVIAL_MOVE_TRAIT(String16) - -static inline std::ostream& operator<<(std::ostream& os, const String16& str) { - os << String8(str); - return os; -} - -// --------------------------------------------------------------------------- - -/* - * A StaticString16 object is a specialized String16 object. Instead of holding - * the string data in a ref counted SharedBuffer object, it holds data in a - * buffer within StaticString16 itself. Note that this buffer is NOT ref - * counted and is assumed to be available for as long as there is at least a - * String16 object using it. Therefore, one must be extra careful to NEVER - * assign a StaticString16 to a String16 that outlives the StaticString16 - * object. - * - * THE SAFEST APPROACH IS TO USE StaticString16 ONLY AS GLOBAL VARIABLES. - * - * A StaticString16 SHOULD NEVER APPEAR IN APIs. USE String16 INSTEAD. - */ -template -class StaticString16 : public String16 { -public: - constexpr StaticString16(const char16_t (&s)[N]) : String16(mData), mData(makeStaticData(s)) {} - - constexpr StaticString16(const StaticString16& other) - : String16(mData), mData(other.mData) {} - - constexpr StaticString16(const StaticString16&&) = delete; - - // There is no reason why one would want to 'new' a StaticString16. Delete - // it to discourage misuse. - static void* operator new(std::size_t) = delete; - -private: - const StaticData mData; -}; - -template -StaticString16(const F&)->StaticString16; - -// --------------------------------------------------------------------------- -// No user servicable parts below. - -inline int compare_type(const String16& lhs, const String16& rhs) -{ - return lhs.compare(rhs); -} - -inline int strictly_order_type(const String16& lhs, const String16& rhs) -{ - return compare_type(lhs, rhs) < 0; -} - -inline const char16_t* String16::c_str() const -{ - return mString; -} - -inline const char16_t* String16::string() const -{ - return mString; -} - -inline bool String16::empty() const -{ - return length() == 0; -} - -inline size_t String16::length() const -{ - return size(); -} - -inline bool String16::contains(const String16& other) const -{ - return contains(other.c_str()); -} - -inline String16& String16::operator=(const String16& other) -{ - setTo(other); - return *this; -} - -inline String16& String16::operator+=(const String16& other) -{ - append(other); - return *this; -} - -inline String16 String16::operator+(const String16& other) const -{ - String16 tmp(*this); - tmp += other; - return tmp; -} - -inline int String16::compare(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()); -} - -inline bool String16::operator<(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) < 0; -} - -inline bool String16::operator<=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) <= 0; -} - -inline bool String16::operator==(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) == 0; -} - -inline bool String16::operator!=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) != 0; -} - -inline bool String16::operator>=(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) >= 0; -} - -inline bool String16::operator>(const String16& other) const -{ - return strzcmp16(mString, size(), other.mString, other.size()) > 0; -} - -#if __cplusplus >= 202002L -inline std::strong_ordering String16::operator<=>(const String16& other) const { - int result = strzcmp16(mString, size(), other.mString, other.size()); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } -} -#endif - -inline bool String16::operator<(const char16_t* other) const -{ - return strcmp16(mString, other) < 0; -} - -inline bool String16::operator<=(const char16_t* other) const -{ - return strcmp16(mString, other) <= 0; -} - -inline bool String16::operator==(const char16_t* other) const -{ - return strcmp16(mString, other) == 0; -} - -inline bool String16::operator!=(const char16_t* other) const -{ - return strcmp16(mString, other) != 0; -} - -inline bool String16::operator>=(const char16_t* other) const -{ - return strcmp16(mString, other) >= 0; -} - -inline bool String16::operator>(const char16_t* other) const -{ - return strcmp16(mString, other) > 0; -} - -#if __cplusplus >= 202002L -inline std::strong_ordering String16::operator<=>(const char16_t* other) const { - int result = strcmp16(mString, other); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } -} -#endif - -inline String16::operator const char16_t*() const -{ - return mString; -} - -inline String16::operator std::u16string_view() const -{ - return {mString, length()}; -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING16_H diff --git a/sysbridge/src/main/cpp/android/utils/String8.cpp b/sysbridge/src/main/cpp/android/utils/String8.cpp deleted file mode 100644 index 3f8d9d634a..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String8.cpp +++ /dev/null @@ -1,453 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "String8.h" - -#include -#include "String16.h" - -#include -#include - -#include -#include -#include - -#include "SharedBuffer.h" - -/* - * Functions outside android is below the namespace android, since they use - * functions and constants in android namespace. - */ - -// --------------------------------------------------------------------------- - -namespace android { - - static inline char *getEmptyString() { - static SharedBuffer *gEmptyStringBuf = [] { - SharedBuffer *buf = SharedBuffer::alloc(1); - char *str = static_cast(buf->data()); - *str = 0; - return buf; - }(); - - gEmptyStringBuf->acquire(); - return static_cast(gEmptyStringBuf->data()); - } - -// --------------------------------------------------------------------------- - - static char *allocFromUTF8(const char *in, size_t len) { - if (len > 0) { - if (len == SIZE_MAX) { - return nullptr; - } - SharedBuffer *buf = SharedBuffer::alloc(len + 1); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (buf) { - char *str = (char *) buf->data(); - memcpy(str, in, len); - str[len] = 0; - return str; - } - return nullptr; - } - - return getEmptyString(); - } - - static char *allocFromUTF16(const char16_t *in, size_t len) { - if (len == 0) return getEmptyString(); - - // Allow for closing '\0' - const ssize_t resultStrLen = utf16_to_utf8_length(in, len) + 1; - if (resultStrLen < 1) { - return getEmptyString(); - } - - SharedBuffer *buf = SharedBuffer::alloc(resultStrLen); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char *resultStr = (char *) buf->data(); - utf16_to_utf8(in, len, resultStr, resultStrLen); - return resultStr; - } - - static char *allocFromUTF32(const char32_t *in, size_t len) { - if (len == 0) { - return getEmptyString(); - } - - const ssize_t resultStrLen = utf32_to_utf8_length(in, len) + 1; - if (resultStrLen < 1) { - return getEmptyString(); - } - - SharedBuffer *buf = SharedBuffer::alloc(resultStrLen); -// ALOG_ASSERT(buf, "Unable to allocate shared buffer"); - if (!buf) { - return getEmptyString(); - } - - char *resultStr = (char *) buf->data(); - utf32_to_utf8(in, len, resultStr, resultStrLen); - - return resultStr; - } - -// --------------------------------------------------------------------------- - - String8::String8() - : mString(getEmptyString()) { - } - - String8::String8(const String8 &o) - : mString(o.mString) { - SharedBuffer::bufferFromData(mString)->acquire(); - } - - String8::String8(const char *o) - : mString(allocFromUTF8(o, strlen(o))) { - if (mString == nullptr) { - mString = getEmptyString(); - } - } - - String8::String8(const char *o, size_t len) - : mString(allocFromUTF8(o, len)) { - if (mString == nullptr) { - mString = getEmptyString(); - } - } - - String8::String8(const String16 &o) : mString(allocFromUTF16(o.c_str(), o.size())) {} - - String8::String8(const char16_t *o) - : mString(allocFromUTF16(o, strlen16(o))) { - } - - String8::String8(const char16_t *o, size_t len) - : mString(allocFromUTF16(o, len)) { - } - - String8::String8(const char32_t *o) - : mString(allocFromUTF32(o, std::char_traits::length(o))) {} - - String8::String8(const char32_t *o, size_t len) - : mString(allocFromUTF32(o, len)) { - } - - String8::~String8() { - SharedBuffer::bufferFromData(mString)->release(); - } - - size_t String8::length() const { - return SharedBuffer::sizeFromData(mString) - 1; - } - - String8 String8::format(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - - String8 result(formatV(fmt, args)); - - va_end(args); - return result; - } - - String8 String8::formatV(const char *fmt, va_list args) { - String8 result; - result.appendFormatV(fmt, args); - return result; - } - - void String8::clear() { - SharedBuffer::bufferFromData(mString)->release(); - mString = getEmptyString(); - } - - void String8::setTo(const String8 &other) { - SharedBuffer::bufferFromData(other.mString)->acquire(); - SharedBuffer::bufferFromData(mString)->release(); - mString = other.mString; - } - - status_t String8::setTo(const char *other) { - const char *newString = allocFromUTF8(other, strlen(other)); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char *other, size_t len) { - const char *newString = allocFromUTF8(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char16_t *other, size_t len) { - const char *newString = allocFromUTF16(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::setTo(const char32_t *other, size_t len) { - const char *newString = allocFromUTF32(other, len); - SharedBuffer::bufferFromData(mString)->release(); - mString = newString; - if (mString) return OK; - - mString = getEmptyString(); - return NO_MEMORY; - } - - status_t String8::append(const String8 &other) { - const size_t otherLen = other.bytes(); - if (bytes() == 0) { - setTo(other); - return OK; - } else if (otherLen == 0) { - return OK; - } - - return real_append(other.c_str(), otherLen); - } - - status_t String8::append(const char *other) { - return append(other, strlen(other)); - } - - status_t String8::append(const char *other, size_t otherLen) { - if (bytes() == 0) { - return setTo(other, otherLen); - } else if (otherLen == 0) { - return OK; - } - - return real_append(other, otherLen); - } - - status_t String8::appendFormat(const char *fmt, ...) { - va_list args; - va_start(args, fmt); - - status_t result = appendFormatV(fmt, args); - - va_end(args); - return result; - } - - status_t String8::appendFormatV(const char *fmt, va_list args) { - int n, result = OK; - va_list tmp_args; - - /* args is undefined after vsnprintf. - * So we need a copy here to avoid the - * second vsnprintf access undefined args. - */ - va_copy(tmp_args, args); - n = vsnprintf(nullptr, 0, fmt, tmp_args); - va_end(tmp_args); - - if (n < 0) return UNKNOWN_ERROR; - - if (n > 0) { - size_t oldLength = length(); - if (static_cast(n) > std::numeric_limits::max() - 1 || - oldLength > std::numeric_limits::max() - n - 1) { - return NO_MEMORY; - } - char *buf = lockBuffer(oldLength + n); - if (buf) { - vsnprintf(buf + oldLength, n + 1, fmt, args); - } else { - result = NO_MEMORY; - } - } - return result; - } - - status_t String8::real_append(const char *other, size_t otherLen) { - const size_t myLen = bytes(); - - SharedBuffer *buf; - size_t newLen; - if (__builtin_add_overflow(myLen, otherLen, &newLen) || - __builtin_add_overflow(newLen, 1, &newLen) || - (buf = SharedBuffer::bufferFromData(mString)->editResize(newLen)) == nullptr) { - return NO_MEMORY; - } - - char *str = (char *) buf->data(); - mString = str; - str += myLen; - memcpy(str, other, otherLen); - str[otherLen] = '\0'; - return OK; - } - - char *String8::lockBuffer(size_t size) { - SharedBuffer *buf = SharedBuffer::bufferFromData(mString) - ->editResize(size + 1); - if (buf) { - char *str = (char *) buf->data(); - mString = str; - return str; - } - return nullptr; - } - - void String8::unlockBuffer() { - unlockBuffer(strlen(mString)); - } - - status_t String8::unlockBuffer(size_t size) { - if (size != this->size()) { - SharedBuffer *buf = SharedBuffer::bufferFromData(mString) - ->editResize(size + 1); - if (!buf) { - return NO_MEMORY; - } - - char *str = (char *) buf->data(); - str[size] = 0; - mString = str; - } - - return OK; - } - - ssize_t String8::find(const char *other, size_t start) const { - size_t len = size(); - if (start >= len) { - return -1; - } - const char *s = mString + start; - const char *p = strstr(s, other); - return p ? p - mString : -1; - } - - bool String8::removeAll(const char *other) { -// ALOG_ASSERT(other, "String8::removeAll() requires a non-NULL string"); - - if (*other == '\0') - return true; - - ssize_t index = find(other); - if (index < 0) return false; - - char *buf = lockBuffer(size()); - if (!buf) return false; // out of memory - - size_t skip = strlen(other); - size_t len = size(); - size_t tail = index; - while (size_t(index) < len) { - ssize_t next = find(other, index + skip); - if (next < 0) { - next = len; - } - - memmove(buf + tail, buf + index + skip, next - index - skip); - tail += next - index - skip; - index = next; - } - unlockBuffer(tail); - return true; - } - - void String8::toLower() { - const size_t length = size(); - if (length == 0) return; - - char *buf = lockBuffer(length); - for (size_t i = length; i > 0; --i) { - *buf = static_cast(tolower(*buf)); - buf++; - } - unlockBuffer(length); - } - -// --------------------------------------------------------------------------- -// Path functions - -// TODO: we should remove all the path functions from String8 -#if defined(_WIN32) -#define OS_PATH_SEPARATOR '\\' -#else -#define OS_PATH_SEPARATOR '/' -#endif - - String8 String8::getPathDir(void) const { - const char *cp; - const char *const str = mString; - - cp = strrchr(str, OS_PATH_SEPARATOR); - if (cp == nullptr) - return String8(""); - else - return String8(str, cp - str); - } - -/* - * Helper function for finding the start of an extension in a pathname. - * - * Returns a pointer inside mString, or NULL if no extension was found. - */ - static const char *find_extension(const char *str) { - const char *lastSlash; - const char *lastDot; - - // only look at the filename - lastSlash = strrchr(str, OS_PATH_SEPARATOR); - if (lastSlash == nullptr) - lastSlash = str; - else - lastSlash++; - - // find the last dot - lastDot = strrchr(lastSlash, '.'); - if (lastDot == nullptr) - return nullptr; - - // looks good, ship it - return lastDot; - } - - String8 String8::getPathExtension(void) const { - auto ext = find_extension(mString); - if (ext != nullptr) - return String8(ext); - else - return String8(""); - } - -}; // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/String8.h b/sysbridge/src/main/cpp/android/utils/String8.h deleted file mode 100644 index 8875750834..0000000000 --- a/sysbridge/src/main/cpp/android/utils/String8.h +++ /dev/null @@ -1,381 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_STRING8_H -#define ANDROID_STRING8_H - -#include -#include -#include // for strcmp -#include -#include -#include "Errors.h" -#include "Unicode.h" -#include "TypeHelpers.h" - -// --------------------------------------------------------------------------- - -namespace android { - - class String16; - -// DO NOT USE: please use std::string - -//! This is a string holding UTF-8 characters. Does not allow the value more -// than 0x10FFFF, which is not valid unicode codepoint. - class String8 { - public: - String8(); - - String8(const String8 &o); - - explicit String8(const char *o); - - explicit String8(const char *o, size_t numChars); - - explicit String8(std::string_view o); - - explicit String8(const String16 &o); - - explicit String8(const char16_t *o); - - explicit String8(const char16_t *o, size_t numChars); - - explicit String8(const char32_t *o); - - explicit String8(const char32_t *o, size_t numChars); - - ~String8(); - - static String8 format(const char *fmt, ...) __attribute__((format (printf, 1, 2))); - - static String8 formatV(const char *fmt, va_list args); - - inline const char *c_str() const; - - inline size_t size() const; - - inline size_t bytes() const; - - inline bool empty() const; - - size_t length() const; - - void clear(); - - void setTo(const String8 &other); - - status_t setTo(const char *other); - - status_t setTo(const char *other, size_t numChars); - - status_t setTo(const char16_t *other, size_t numChars); - - status_t setTo(const char32_t *other, - size_t length); - - status_t append(const String8 &other); - - status_t append(const char *other); - - status_t append(const char *other, size_t numChars); - - status_t appendFormat(const char *fmt, ...) - __attribute__((format (printf, 2, 3))); - - status_t appendFormatV(const char *fmt, va_list args); - - inline String8 &operator=(const String8 &other); - - inline String8 &operator=(const char *other); - - inline String8 &operator+=(const String8 &other); - - inline String8 operator+(const String8 &other) const; - - inline String8 &operator+=(const char *other); - - inline String8 operator+(const char *other) const; - - inline int compare(const String8 &other) const; - - inline bool operator<(const String8 &other) const; - - inline bool operator<=(const String8 &other) const; - - inline bool operator==(const String8 &other) const; - - inline bool operator!=(const String8 &other) const; - - inline bool operator>=(const String8 &other) const; - - inline bool operator>(const String8 &other) const; - -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const String8& other) const; -#endif - - inline bool operator<(const char *other) const; - - inline bool operator<=(const char *other) const; - - inline bool operator==(const char *other) const; - - inline bool operator!=(const char *other) const; - - inline bool operator>=(const char *other) const; - - inline bool operator>(const char *other) const; - -#if __cplusplus >= 202002L - inline std::strong_ordering operator<=>(const char* other) const; -#endif - - inline operator const char *() const; - - inline explicit operator std::string_view() const; - - char *lockBuffer(size_t size); - - void unlockBuffer(); - - status_t unlockBuffer(size_t size); - - // return the index of the first byte of other in this at or after - // start, or -1 if not found - ssize_t find(const char *other, size_t start = 0) const; - - inline ssize_t find(const String8 &other, size_t start = 0) const; - - // return true if this string contains the specified substring - inline bool contains(const char *other) const; - - inline bool contains(const String8 &other) const; - - // removes all occurrence of the specified substring - // returns true if any were found and removed - bool removeAll(const char *other); - - inline bool removeAll(const String8 &other); - - void toLower(); - - private: - String8 getPathDir(void) const; - - String8 getPathExtension(void) const; - - status_t real_append(const char *other, size_t numChars); - - const char *mString; - -// These symbols are for potential backward compatibility with prebuilts. To be removed. -#ifdef ENABLE_STRING8_OBSOLETE_METHODS - public: -#else - private: -#endif - - inline const char *string() const; - - inline bool isEmpty() const; - }; - -// String8 can be trivially moved using memcpy() because moving does not -// require any change to the underlying SharedBuffer contents or reference count. - ANDROID_TRIVIAL_MOVE_TRAIT(String8) - - static inline std::ostream &operator<<(std::ostream &os, const String8 &str) { - os << str.c_str(); - return os; - } - -// --------------------------------------------------------------------------- -// No user servicable parts below. - - inline int compare_type(const String8 &lhs, const String8 &rhs) { - return lhs.compare(rhs); - } - - inline int strictly_order_type(const String8 &lhs, const String8 &rhs) { - return compare_type(lhs, rhs) < 0; - } - - inline const char *String8::c_str() const { - return mString; - } - - inline const char *String8::string() const { - return mString; - } - - inline size_t String8::size() const { - return length(); - } - - inline bool String8::empty() const { - return length() == 0; - } - - inline bool String8::isEmpty() const { - return length() == 0; - } - - inline size_t String8::bytes() const { - return length(); - } - - inline ssize_t String8::find(const String8 &other, size_t start) const { - return find(other.c_str(), start); - } - - inline bool String8::contains(const char *other) const { - return find(other) >= 0; - } - - inline bool String8::contains(const String8 &other) const { - return contains(other.c_str()); - } - - inline bool String8::removeAll(const String8 &other) { - return removeAll(other.c_str()); - } - - inline String8 &String8::operator=(const String8 &other) { - setTo(other); - return *this; - } - - inline String8 &String8::operator=(const char *other) { - setTo(other); - return *this; - } - - inline String8 &String8::operator+=(const String8 &other) { - append(other); - return *this; - } - - inline String8 String8::operator+(const String8 &other) const { - String8 tmp(*this); - tmp += other; - return tmp; - } - - inline String8 &String8::operator+=(const char *other) { - append(other); - return *this; - } - - inline String8 String8::operator+(const char *other) const { - String8 tmp(*this); - tmp += other; - return tmp; - } - - inline int String8::compare(const String8 &other) const { - return strcmp(mString, other.mString); - } - - inline bool String8::operator<(const String8 &other) const { - return strcmp(mString, other.mString) < 0; - } - - inline bool String8::operator<=(const String8 &other) const { - return strcmp(mString, other.mString) <= 0; - } - - inline bool String8::operator==(const String8 &other) const { - return strcmp(mString, other.mString) == 0; - } - - inline bool String8::operator!=(const String8 &other) const { - return strcmp(mString, other.mString) != 0; - } - - inline bool String8::operator>=(const String8 &other) const { - return strcmp(mString, other.mString) >= 0; - } - - inline bool String8::operator>(const String8 &other) const { - return strcmp(mString, other.mString) > 0; - } - -#if __cplusplus >= 202002L - inline std::strong_ordering String8::operator<=>(const String8& other) const { - int result = strcmp(mString, other.mString); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } - } -#endif - - inline bool String8::operator<(const char *other) const { - return strcmp(mString, other) < 0; - } - - inline bool String8::operator<=(const char *other) const { - return strcmp(mString, other) <= 0; - } - - inline bool String8::operator==(const char *other) const { - return strcmp(mString, other) == 0; - } - - inline bool String8::operator!=(const char *other) const { - return strcmp(mString, other) != 0; - } - - inline bool String8::operator>=(const char *other) const { - return strcmp(mString, other) >= 0; - } - - inline bool String8::operator>(const char *other) const { - return strcmp(mString, other) > 0; - } - -#if __cplusplus >= 202002L - inline std::strong_ordering String8::operator<=>(const char* other) const { - int result = strcmp(mString, other); - if (result == 0) { - return std::strong_ordering::equal; - } else if (result < 0) { - return std::strong_ordering::less; - } else { - return std::strong_ordering::greater; - } - } -#endif - - inline String8::operator const char *() const { - return mString; - } - - inline String8::String8(std::string_view o) : String8(o.data(), o.length()) {} - - inline String8::operator std::string_view() const { - return {mString, length()}; - } - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_STRING8_H diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp b/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp deleted file mode 100644 index e9ad7bebf3..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Tokenizer.cpp +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#include "../../logging.h" -#include "Tokenizer.h" -#include "Errors.h" -#include "FileMap.h" -#include "String8.h" -#include -#include -#include -#include - -#ifndef DEBUG_TOKENIZER -// Enables debug output for the tokenizer. -#define DEBUG_TOKENIZER 0 -#endif - -namespace android { - - static inline bool isDelimiter(char ch, const char *delimiters) { - return strchr(delimiters, ch) != nullptr; - } - - Tokenizer::Tokenizer(const String8 &filename, FileMap *fileMap, char *buffer, - bool ownBuffer, size_t length) : - mFilename(filename), mFileMap(fileMap), - mBuffer(buffer), mOwnBuffer(ownBuffer), mLength(length), - mCurrent(buffer), mLineNumber(1) { - } - - Tokenizer::~Tokenizer() { - delete mFileMap; - if (mOwnBuffer) { - delete[] mBuffer; - } - } - - status_t Tokenizer::open(const String8 &filename, Tokenizer **outTokenizer) { - *outTokenizer = nullptr; - - int result = OK; - int fd = ::open(filename.c_str(), O_RDONLY); - if (fd < 0) { - result = -errno; - LOGE("Error opening file '%s': %s", filename.c_str(), strerror(errno)); - } else { - struct stat stat; - if (fstat(fd, &stat)) { - result = -errno; - LOGE("Error getting size of file '%s': %s", filename.c_str(), strerror(errno)); - } else { - size_t length = size_t(stat.st_size); - - FileMap *fileMap = new FileMap(); - bool ownBuffer = false; - char *buffer; - if (fileMap->create(nullptr, fd, 0, length, true)) { - fileMap->advise(FileMap::SEQUENTIAL); - buffer = static_cast(fileMap->getDataPtr()); - } else { - delete fileMap; - fileMap = nullptr; - - // Fall back to reading into a buffer since we can't mmap files in sysfs. - // The length we obtained from stat is wrong too (it will always be 4096) - // so we must trust that read will read the entire file. - buffer = new char[length]; - ownBuffer = true; - ssize_t nrd = read(fd, buffer, length); - if (nrd < 0) { - result = -errno; - LOGE("Error reading file '%s': %s", filename.c_str(), strerror(errno)); - delete[] buffer; - buffer = nullptr; - } else { - length = size_t(nrd); - } - } - - if (!result) { - *outTokenizer = new Tokenizer(filename, fileMap, buffer, ownBuffer, length); - } - } - close(fd); - } - return result; - } - - status_t Tokenizer::fromContents(const String8 &filename, - const char *contents, Tokenizer **outTokenizer) { - *outTokenizer = new Tokenizer(filename, nullptr, - const_cast(contents), false, strlen(contents)); - return OK; - } - - String8 Tokenizer::getLocation() const { - String8 result; - result.appendFormat("%s:%d", mFilename.c_str(), mLineNumber); - return result; - } - - String8 Tokenizer::peekRemainderOfLine() const { - const char *end = getEnd(); - const char *eol = mCurrent; - while (eol != end) { - char ch = *eol; - if (ch == '\n') { - break; - } - eol += 1; - } - return String8(mCurrent, eol - mCurrent); - } - - String8 Tokenizer::nextToken(const char *delimiters) { -#if DEBUG_TOKENIZER - LOGD("nextToken"); -#endif - const char *end = getEnd(); - const char *tokenStart = mCurrent; - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } - return String8(tokenStart, mCurrent - tokenStart); - } - - void Tokenizer::nextLine() { -#if DEBUG_TOKENIZER - LOGD("nextLine"); -#endif - const char *end = getEnd(); - while (mCurrent != end) { - char ch = *(mCurrent++); - if (ch == '\n') { - mLineNumber += 1; - break; - } - } - } - - void Tokenizer::skipDelimiters(const char *delimiters) { -#if DEBUG_TOKENIZER - LOGD("skipDelimiters"); -#endif - const char *end = getEnd(); - while (mCurrent != end) { - char ch = *mCurrent; - if (ch == '\n' || !isDelimiter(ch, delimiters)) { - break; - } - mCurrent += 1; - } - } - -} // namespace android diff --git a/sysbridge/src/main/cpp/android/utils/Tokenizer.h b/sysbridge/src/main/cpp/android/utils/Tokenizer.h deleted file mode 100644 index dee36a7bb0..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Tokenizer.h +++ /dev/null @@ -1,137 +0,0 @@ -/* - * Copyright (C) 2010 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef _UTILS_TOKENIZER_H -#define _UTILS_TOKENIZER_H - -#include -#include -#include "FileMap.h" -#include "Errors.h" -#include "String8.h" - -namespace android { - -/** - * A simple tokenizer for loading and parsing ASCII text files line by line. - */ -class Tokenizer { - Tokenizer(const String8& filename, FileMap* fileMap, char* buffer, - bool ownBuffer, size_t length); - -public: - ~Tokenizer(); - - /** - * Opens a file and maps it into memory. - * - * Returns OK and a tokenizer for the file, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t open(const String8& filename, Tokenizer** outTokenizer); - - /** - * Prepares to tokenize the contents of a string. - * - * Returns OK and a tokenizer for the string, if successful. - * Otherwise returns an error and sets outTokenizer to NULL. - */ - static status_t fromContents(const String8& filename, - const char* contents, Tokenizer** outTokenizer); - - /** - * Returns true if at the end of the file. - */ - inline bool isEof() const { return mCurrent == getEnd(); } - - /** - * Returns true if at the end of the line or end of the file. - */ - inline bool isEol() const { return isEof() || *mCurrent == '\n'; } - - /** - * Gets the name of the file. - */ - inline String8 getFilename() const { return mFilename; } - - /** - * Gets a 1-based line number index for the current position. - */ - inline int32_t getLineNumber() const { return mLineNumber; } - - /** - * Formats a location string consisting of the filename and current line number. - * Returns a string like "MyFile.txt:33". - */ - String8 getLocation() const; - - /** - * Gets the character at the current position. - * Returns null at end of file. - */ - inline char peekChar() const { return isEof() ? '\0' : *mCurrent; } - - /** - * Gets the remainder of the current line as a string, excluding the newline character. - */ - String8 peekRemainderOfLine() const; - - /** - * Gets the character at the current position and advances past it. - * Returns null at end of file. - */ - inline char nextChar() { return isEof() ? '\0' : *(mCurrent++); } - - /** - * Gets the next token on this line stopping at the specified delimiters - * or the end of the line whichever comes first and advances past it. - * Also stops at embedded nulls. - * Returns the token or an empty string if the current character is a delimiter - * or is at the end of the line. - */ - String8 nextToken(const char* delimiters); - - /** - * Advances to the next line. - * Does nothing if already at the end of the file. - */ - void nextLine(); - - /** - * Skips over the specified delimiters in the line. - * Also skips embedded nulls. - */ - void skipDelimiters(const char* delimiters); - -private: - Tokenizer(const Tokenizer& other); // not copyable - - String8 mFilename; - FileMap* mFileMap; - char* mBuffer; - bool mOwnBuffer; - size_t mLength; - - const char* mCurrent; - int32_t mLineNumber; - - inline const char* getEnd() const { return mBuffer + mLength; } - -}; - -} // namespace android - -#endif // _UTILS_TOKENIZER_H diff --git a/sysbridge/src/main/cpp/android/utils/TypeHelpers.h b/sysbridge/src/main/cpp/android/utils/TypeHelpers.h deleted file mode 100644 index d867a9a46c..0000000000 --- a/sysbridge/src/main/cpp/android/utils/TypeHelpers.h +++ /dev/null @@ -1,342 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_TYPE_HELPERS_H -#define ANDROID_TYPE_HELPERS_H - -#include -#include - -#include -#include -#include -#include - -// --------------------------------------------------------------------------- - -namespace android { - -/* - * Types traits - */ - -template struct trait_trivial_ctor { enum { value = false }; }; -template struct trait_trivial_dtor { enum { value = false }; }; -template struct trait_trivial_copy { enum { value = false }; }; -template struct trait_trivial_move { enum { value = false }; }; -template struct trait_pointer { enum { value = false }; }; -template struct trait_pointer { enum { value = true }; }; - -template -struct traits { - enum { - // whether this type is a pointer - is_pointer = trait_pointer::value, - // whether this type's constructor is a no-op - has_trivial_ctor = is_pointer || trait_trivial_ctor::value, - // whether this type's destructor is a no-op - has_trivial_dtor = is_pointer || trait_trivial_dtor::value, - // whether this type type can be copy-constructed with memcpy - has_trivial_copy = is_pointer || trait_trivial_copy::value, - // whether this type can be moved with memmove - has_trivial_move = is_pointer || trait_trivial_move::value - }; -}; - -template -struct aggregate_traits { - enum { - is_pointer = false, - has_trivial_ctor = - traits::has_trivial_ctor && traits::has_trivial_ctor, - has_trivial_dtor = - traits::has_trivial_dtor && traits::has_trivial_dtor, - has_trivial_copy = - traits::has_trivial_copy && traits::has_trivial_copy, - has_trivial_move = - traits::has_trivial_move && traits::has_trivial_move - }; -}; - -#define ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - template<> struct trait_trivial_ctor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - template<> struct trait_trivial_dtor< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - template<> struct trait_trivial_copy< T > { enum { value = true }; }; - -#define ANDROID_TRIVIAL_MOVE_TRAIT( T ) \ - template<> struct trait_trivial_move< T > { enum { value = true }; }; - -#define ANDROID_BASIC_TYPES_TRAITS( T ) \ - ANDROID_TRIVIAL_CTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_DTOR_TRAIT( T ) \ - ANDROID_TRIVIAL_COPY_TRAIT( T ) \ - ANDROID_TRIVIAL_MOVE_TRAIT( T ) - -// --------------------------------------------------------------------------- - -/* - * basic types traits - */ - -ANDROID_BASIC_TYPES_TRAITS( void ) -ANDROID_BASIC_TYPES_TRAITS( bool ) -ANDROID_BASIC_TYPES_TRAITS( char ) -ANDROID_BASIC_TYPES_TRAITS( unsigned char ) -ANDROID_BASIC_TYPES_TRAITS( short ) -ANDROID_BASIC_TYPES_TRAITS( unsigned short ) -ANDROID_BASIC_TYPES_TRAITS( int ) -ANDROID_BASIC_TYPES_TRAITS( unsigned int ) -ANDROID_BASIC_TYPES_TRAITS( long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long ) -ANDROID_BASIC_TYPES_TRAITS( long long ) -ANDROID_BASIC_TYPES_TRAITS( unsigned long long ) -ANDROID_BASIC_TYPES_TRAITS( float ) -ANDROID_BASIC_TYPES_TRAITS( double ) - -template struct trait_trivial_ctor { enum { value = true }; }; -template struct trait_trivial_dtor { enum { value = true }; }; -template struct trait_trivial_copy { enum { value = true }; }; -template struct trait_trivial_move { enum { value = true }; }; - -// --------------------------------------------------------------------------- - - -/* - * compare and order types - */ - -template inline -int strictly_order_type(const TYPE& lhs, const TYPE& rhs) { - return (lhs < rhs) ? 1 : 0; -} - -template inline -int compare_type(const TYPE& lhs, const TYPE& rhs) { - return strictly_order_type(rhs, lhs) - strictly_order_type(lhs, rhs); -} - -/* - * create, destroy, copy and move types... - */ - -template inline -void construct_type(TYPE* p, size_t n) { - if (!traits::has_trivial_ctor) { - while (n > 0) { - n--; - new(p++) TYPE; - } - } -} - -template inline -void destroy_type(TYPE* p, size_t n) { - if (!traits::has_trivial_dtor) { - while (n > 0) { - n--; - p->~TYPE(); - p++; - } - } -} - -template -typename std::enable_if::has_trivial_copy>::type -inline -copy_type(TYPE* d, const TYPE* s, size_t n) { - memcpy(d,s,n*sizeof(TYPE)); -} - -template -typename std::enable_if::has_trivial_copy>::type -inline -copy_type(TYPE* d, const TYPE* s, size_t n) { - while (n > 0) { - n--; - new(d) TYPE(*s); - d++, s++; - } -} - -template inline -void splat_type(TYPE* where, const TYPE* what, size_t n) { - if (!traits::has_trivial_copy) { - while (n > 0) { - n--; - new(where) TYPE(*what); - where++; - } - } else { - while (n > 0) { - n--; - *where++ = *what; - } - } -} - -template -struct use_trivial_move : public std::integral_constant::has_trivial_dtor && traits::has_trivial_copy) - || traits::has_trivial_move -> {}; - -template -typename std::enable_if::value>::type -inline -move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - memmove(reinterpret_cast(d), s, n * sizeof(TYPE)); -} - -template -typename std::enable_if::value>::type -inline -move_forward_type(TYPE* d, const TYPE* s, size_t n = 1) { - d += n; - s += n; - while (n > 0) { - n--; - --d, --s; - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - } -} - -template -typename std::enable_if::value>::type -inline -move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - memmove(reinterpret_cast(d), s, n * sizeof(TYPE)); -} - -template -typename std::enable_if::value>::type -inline -move_backward_type(TYPE* d, const TYPE* s, size_t n = 1) { - while (n > 0) { - n--; - if (!traits::has_trivial_copy) { - new(d) TYPE(*s); - } else { - *d = *s; - } - if (!traits::has_trivial_dtor) { - s->~TYPE(); - } - d++, s++; - } -} - -// --------------------------------------------------------------------------- - -/* - * a key/value pair - */ - -template -struct key_value_pair_t { - typedef KEY key_t; - typedef VALUE value_t; - - KEY key; - VALUE value; - key_value_pair_t() { } - key_value_pair_t(const key_value_pair_t& o) : key(o.key), value(o.value) { } - key_value_pair_t& operator=(const key_value_pair_t& o) { - key = o.key; - value = o.value; - return *this; - } - key_value_pair_t(const KEY& k, const VALUE& v) : key(k), value(v) { } - explicit key_value_pair_t(const KEY& k) : key(k) { } - inline bool operator < (const key_value_pair_t& o) const { - return strictly_order_type(key, o.key); - } - inline const KEY& getKey() const { - return key; - } - inline const VALUE& getValue() const { - return value; - } -}; - -template -struct trait_trivial_ctor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_ctor }; }; -template -struct trait_trivial_dtor< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_dtor }; }; -template -struct trait_trivial_copy< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_copy }; }; -template -struct trait_trivial_move< key_value_pair_t > -{ enum { value = aggregate_traits::has_trivial_move }; }; - -// --------------------------------------------------------------------------- - -/* - * Hash codes. - */ -typedef uint32_t hash_t; - -template -hash_t hash_type(const TKey& key); - -/* Built-in hash code specializations */ -#define ANDROID_INT32_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { return hash_t(value); } -#define ANDROID_INT64_HASH(T) \ - template <> inline hash_t hash_type(const T& value) { \ - return hash_t((value >> 32) ^ value); } -#define ANDROID_REINTERPRET_HASH(T, R) \ - template <> inline hash_t hash_type(const T& value) { \ - R newValue; \ - static_assert(sizeof(newValue) == sizeof(value), "size mismatch"); \ - memcpy(&newValue, &value, sizeof(newValue)); \ - return hash_type(newValue); \ - } - -ANDROID_INT32_HASH(bool) -ANDROID_INT32_HASH(int8_t) -ANDROID_INT32_HASH(uint8_t) -ANDROID_INT32_HASH(int16_t) -ANDROID_INT32_HASH(uint16_t) -ANDROID_INT32_HASH(int32_t) -ANDROID_INT32_HASH(uint32_t) -ANDROID_INT64_HASH(int64_t) -ANDROID_INT64_HASH(uint64_t) -ANDROID_REINTERPRET_HASH(float, uint32_t) -ANDROID_REINTERPRET_HASH(double, uint64_t) - -template inline hash_t hash_type(T* const & value) { - return hash_type(uintptr_t(value)); -} - -} // namespace android - -// --------------------------------------------------------------------------- - -#endif // ANDROID_TYPE_HELPERS_H diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.cpp b/sysbridge/src/main/cpp/android/utils/Unicode.cpp deleted file mode 100644 index 49179cc181..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Unicode.cpp +++ /dev/null @@ -1,538 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#define LOG_TAG "unicode" - -#include -#include "Unicode.h" -#include "../../logging.h" -#include "../liblog/log_main.h" - -#include - -extern "C" { - -static const char32_t kByteMask = 0x000000BF; -static const char32_t kByteMark = 0x00000080; - -// Surrogates aren't valid for UTF-32 characters, so define some -// constants that will let us screen them out. -static const char32_t kUnicodeSurrogateHighStart = 0x0000D800; -// Unused, here for completeness: -// static const char32_t kUnicodeSurrogateHighEnd = 0x0000DBFF; -// static const char32_t kUnicodeSurrogateLowStart = 0x0000DC00; -static const char32_t kUnicodeSurrogateLowEnd = 0x0000DFFF; -static const char32_t kUnicodeSurrogateStart = kUnicodeSurrogateHighStart; -static const char32_t kUnicodeSurrogateEnd = kUnicodeSurrogateLowEnd; -static const char32_t kUnicodeMaxCodepoint = 0x0010FFFF; - -// Mask used to set appropriate bits in first byte of UTF-8 sequence, -// indexed by number of bytes in the sequence. -// 0xxxxxxx -// -> (00-7f) 7bit. Bit mask for the first byte is 0x00000000 -// 110yyyyx 10xxxxxx -// -> (c0-df)(80-bf) 11bit. Bit mask is 0x000000C0 -// 1110yyyy 10yxxxxx 10xxxxxx -// -> (e0-ef)(80-bf)(80-bf) 16bit. Bit mask is 0x000000E0 -// 11110yyy 10yyxxxx 10xxxxxx 10xxxxxx -// -> (f0-f7)(80-bf)(80-bf)(80-bf) 21bit. Bit mask is 0x000000F0 -static const char32_t kFirstByteMark[] = { - 0x00000000, 0x00000000, 0x000000C0, 0x000000E0, 0x000000F0 -}; - -// -------------------------------------------------------------------------- -// UTF-32 -// -------------------------------------------------------------------------- - -/** - * Return number of UTF-8 bytes required for the character. If the character - * is invalid, return size of 0. - */ -static inline size_t utf32_codepoint_utf8_length(char32_t srcChar) { - // Figure out how many bytes the result will require. - if (srcChar < 0x00000080) { - return 1; - } else if (srcChar < 0x00000800) { - return 2; - } else if (srcChar < 0x00010000) { - if ((srcChar < kUnicodeSurrogateStart) || (srcChar > kUnicodeSurrogateEnd)) { - return 3; - } else { - // Surrogates are invalid UTF-32 characters. - return 0; - } - } - // Max code point for Unicode is 0x0010FFFF. - else if (srcChar <= kUnicodeMaxCodepoint) { - return 4; - } else { - // Invalid UTF-32 character. - return 0; - } -} - -// Write out the source character to . - -static inline void utf32_codepoint_to_utf8(uint8_t *dstP, char32_t srcChar, size_t bytes) { - dstP += bytes; - switch (bytes) { /* note: everything falls through. */ - case 4: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 3: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 2: - *--dstP = (uint8_t) ((srcChar | kByteMark) & kByteMask); - srcChar >>= 6; - [[fallthrough]]; - case 1: - *--dstP = (uint8_t) (srcChar | kFirstByteMark[bytes]); - } -} - -static inline int32_t utf32_at_internal(const char *cur, size_t *num_read) { - const char first_char = *cur; - if ((first_char & 0x80) == 0) { // ASCII - *num_read = 1; - return *cur; - } - cur++; - char32_t mask, to_ignore_mask; - size_t num_to_read = 0; - char32_t utf32 = first_char; - for (num_to_read = 1, mask = 0x40, to_ignore_mask = 0xFFFFFF80; - (first_char & mask); - num_to_read++, to_ignore_mask |= mask, mask >>= 1) { - // 0x3F == 00111111 - utf32 = (utf32 << 6) + (*cur++ & 0x3F); - } - to_ignore_mask |= mask; - utf32 &= ~(to_ignore_mask << (6 * (num_to_read - 1))); - - *num_read = num_to_read; - return static_cast(utf32); -} - -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index) { - if (index >= src_len) { - return -1; - } - size_t unused_index; - if (next_index == nullptr) { - next_index = &unused_index; - } - size_t num_read; - int32_t ret = utf32_at_internal(src + index, &num_read); - if (ret >= 0) { - *next_index = index + num_read; - } - - return ret; -} - -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len) { - if (src == nullptr || src_len == 0) { - return -1; - } - - size_t ret = 0; - const char32_t *end = src + src_len; - while (src < end) { - size_t char_len = utf32_codepoint_utf8_length(*src++); - if (SSIZE_MAX - char_len < ret) { - // If this happens, we would overflow the ssize_t type when - // returning from this function, so we cannot express how - // long this string is in an ssize_t. -// android_errorWriteLog(0x534e4554, "37723026"); - return -1; - } - ret += char_len; - } - return ret; -} - -void utf32_to_utf8(const char32_t *src, size_t src_len, char *dst, size_t dst_len) { - if (src == nullptr || src_len == 0 || dst == nullptr) { - return; - } - - const char32_t *cur_utf32 = src; - const char32_t *end_utf32 = src + src_len; - char *cur = dst; - while (cur_utf32 < end_utf32) { - size_t len = utf32_codepoint_utf8_length(*cur_utf32); - LOG_ALWAYS_FATAL_IF(dst_len < len, "%zu < %zu", dst_len, len); - utf32_codepoint_to_utf8((uint8_t *) cur, *cur_utf32++, len); - cur += len; - dst_len -= len; - } - LOG_ALWAYS_FATAL_IF(dst_len < 1, "dst_len < 1: %zu < 1", dst_len); - *cur = '\0'; -} - -// -------------------------------------------------------------------------- -// UTF-16 -// -------------------------------------------------------------------------- - -int strcmp16(const char16_t *s1, const char16_t *s2) { - char16_t ch; - int d = 0; - - while (1) { - d = (int) (ch = *s1++) - (int) *s2++; - if (d || !ch) - break; - } - - return d; -} - -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n) { - char16_t ch; - int d = 0; - - if (n == 0) { - return 0; - } - - do { - d = (int) (ch = *s1++) - (int) *s2++; - if (d || !ch) { - break; - } - } while (--n); - - return d; -} - -size_t strlen16(const char16_t *s) { - const char16_t *ss = s; - while (*ss) - ss++; - return ss - s; -} - -size_t strnlen16(const char16_t *s, size_t maxlen) { - const char16_t *ss = s; - - /* Important: the maxlen test must precede the reference through ss; - since the byte beyond the maximum may segfault */ - while ((maxlen > 0) && *ss) { - ss++; - maxlen--; - } - return ss - s; -} - -char16_t *strstr16(const char16_t *src, const char16_t *target) { - const char16_t needle = *target; - if (needle == '\0') return (char16_t *) src; - - const size_t target_len = strlen16(++target); - do { - do { - if (*src == '\0') { - return nullptr; - } - } while (*src++ != needle); - } while (strncmp16(src, target, target_len) != 0); - src--; - - return (char16_t *) src; -} - -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2) { - const char16_t *e1 = s1 + n1; - const char16_t *e2 = s2 + n2; - - while (s1 < e1 && s2 < e2) { - const int d = (int) *s1++ - (int) *s2++; - if (d) { - return d; - } - } - - return n1 < n2 - ? (0 - (int) *s2) - : (n1 > n2 - ? ((int) *s1 - 0) - : 0); -} - -// is_any_surrogate() returns true if w is either a high or low surrogate -static constexpr bool is_any_surrogate(char16_t w) { - return (w & 0xf800) == 0xd800; -} - -// is_surrogate_pair() returns true if w1 and w2 form a valid surrogate pair -static constexpr bool is_surrogate_pair(char16_t w1, char16_t w2) { - return ((w1 & 0xfc00) == 0xd800) && ((w2 & 0xfc00) == 0xdc00); -} - -// TODO: currently utf16_to_utf8_length() returns -1 if src_len == 0, -// which is inconsistent with utf8_to_utf16_length(), here we keep the -// current behavior as intended not to break compatibility -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len) { - if (src == nullptr || src_len == 0) - return -1; - - const char16_t *const end = src + src_len; - const char16_t *in = src; - size_t utf8_len = 0; - - while (in < end) { - char16_t w = *in++; - if (w < 0x0080) [[likely]] { - utf8_len += 1; - continue; - } - if (w < 0x0800) [[likely]] { - utf8_len += 2; - continue; - } - if (!is_any_surrogate(w)) [[likely]] { - utf8_len += 3; - continue; - } - if (in < end && is_surrogate_pair(w, *in)) { - utf8_len += 4; - in++; - continue; - } - /* skip if at the end of the string or invalid surrogate pair */ - } - return (in == end && utf8_len < SSIZE_MAX) ? utf8_len : -1; -} - -void utf16_to_utf8(const char16_t *src, size_t src_len, char *dst, size_t dst_len) { - if (src == nullptr || src_len == 0 || dst == nullptr) { - return; - } - - const char16_t *in = src; - const char16_t *const in_end = src + src_len; - char *out = dst; - const char *const out_end = dst + dst_len; - char16_t w2; - - auto err_out = [&out, &out_end, &dst_len]() { - LOG_ALWAYS_FATAL_IF(out >= out_end, - "target utf8 string size %zu too short", dst_len); - }; - - while (in < in_end) { - char16_t w = *in++; - if (w < 0x0080) [[likely]] { - if (out + 1 > out_end) - return err_out(); - *out++ = (char) (w & 0xff); - continue; - } - if (w < 0x0800) [[likely]] { - if (out + 2 > out_end) - return err_out(); - *out++ = (char) (0xc0 | ((w >> 6) & 0x1f)); - *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); - continue; - } - if (!is_any_surrogate(w)) [[likely]] { - if (out + 3 > out_end) - return err_out(); - *out++ = (char) (0xe0 | ((w >> 12) & 0xf)); - *out++ = (char) (0x80 | ((w >> 6) & 0x3f)); - *out++ = (char) (0x80 | ((w >> 0) & 0x3f)); - continue; - } - /* surrogate pair */ - if (in < in_end && (w2 = *in, is_surrogate_pair(w, w2))) { - if (out + 4 > out_end) - return err_out(); - char32_t dw = (char32_t) (0x10000 + ((w - 0xd800) << 10) + (w2 - 0xdc00)); - *out++ = (char) (0xf0 | ((dw >> 18) & 0x07)); - *out++ = (char) (0x80 | ((dw >> 12) & 0x3f)); - *out++ = (char) (0x80 | ((dw >> 6) & 0x3f)); - *out++ = (char) (0x80 | ((dw >> 0) & 0x3f)); - in++; - } - /* We reach here in two cases: - * 1) (in == in_end), which means end of the input string - * 2) (w2 & 0xfc00) != 0xdc00, which means invalid surrogate pair - * In either case, we intentionally do nothing and skip - */ - } - *out = '\0'; - return; -} - -// -------------------------------------------------------------------------- -// UTF-8 -// -------------------------------------------------------------------------- - -static char32_t utf8_4b_to_utf32(uint8_t c1, uint8_t c2, uint8_t c3, uint8_t c4) { - return ((c1 & 0x07) << 18) | ((c2 & 0x3f) << 12) | ((c3 & 0x3f) << 6) | (c4 & 0x3f); -} - -// TODO: current behavior of converting UTF8 to UTF-16 has a few issues below -// -// 1. invalid trailing bytes (i.e. not b'10xxxxxx) are treated as valid trailing -// bytes and follows normal conversion rules -// 2. invalid leading byte (b'10xxxxxx) is treated as a valid single UTF-8 byte -// 3. invalid leading byte (b'11111xxx) is treated as a valid leading byte -// (same as b'11110xxx) for a 4-byte UTF-8 sequence -// 4. an invalid 4-byte UTF-8 sequence that translates to a codepoint < U+10000 -// will be converted as a valid UTF-16 character -// -// We keep the current behavior as is but with warnings logged, so as not to -// break compatibility. However, this needs to be addressed later. - -ssize_t utf8_to_utf16_length(const uint8_t *u8str, size_t u8len, bool overreadIsFatal) { - if (u8str == nullptr) - return -1; - - const uint8_t *const in_end = u8str + u8len; - const uint8_t *in = u8str; - size_t utf16_len = 0; - - while (in < in_end) { - uint8_t c = *in; - utf16_len++; - if ((c & 0x80) == 0) [[likely]] { - in++; - continue; - } - if (c < 0xc0) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - in++; - continue; - } - if (c < 0xe0) [[likely]] { - in += 2; - continue; - } - if (c < 0xf0) [[likely]] { - in += 3; - continue; - } else { - uint8_t c2, c3, c4; - if (c >= 0xf8) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - } - c2 = in[1]; - c3 = in[2]; - c4 = in[3]; - if (utf8_4b_to_utf32(c, c2, c3, c4) >= 0x10000) { - utf16_len++; - } - in += 4; - continue; - } - } - if (in == in_end) { - return utf16_len < SSIZE_MAX ? utf16_len : -1; - } - if (overreadIsFatal) - LOG_ALWAYS_FATAL("Attempt to overread computing length of utf8 string"); - return -1; -} - -char16_t *utf8_to_utf16(const uint8_t *u8str, size_t u8len, char16_t *u16str, size_t u16len) { - // A value > SSIZE_MAX is probably a negative value returned as an error and casted. - LOG_ALWAYS_FATAL_IF(u16len == 0 || u16len > SSIZE_MAX, "u16len is %zu", u16len); - char16_t *end = utf8_to_utf16_no_null_terminator(u8str, u8len, u16str, u16len - 1); - *end = 0; - return end; -} - -char16_t *utf8_to_utf16_no_null_terminator( - const uint8_t *src, size_t srcLen, char16_t *dst, size_t dstLen) { - if (src == nullptr || srcLen == 0 || dstLen == 0) { - return dst; - } - // A value > SSIZE_MAX is probably a negative value returned as an error and casted. - LOG_ALWAYS_FATAL_IF(dstLen > SSIZE_MAX, "dstLen is %zu", dstLen); - - const uint8_t *const in_end = src + srcLen; - const uint8_t *in = src; - const char16_t *const out_end = dst + dstLen; - char16_t *out = dst; - uint8_t c, c2, c3, c4; - char32_t w; - - auto err_in = [&c, &out]() { - LOGW("Unended UTF-8 byte: 0x%02x", c); - return out; - }; - - while (in < in_end && out < out_end) { - c = *in++; - if ((c & 0x80) == 0) [[likely]] { - *out++ = (char16_t) (c); - continue; - } - if (c < 0xc0) [[unlikely]] { - ALOGW("Invalid UTF-8 leading byte: 0x%02x", c); - *out++ = (char16_t) (c); - continue; - } - if (c < 0xe0) [[likely]] { - if (in + 1 > in_end) [[unlikely]] { - return err_in(); - } - c2 = *in++; - *out++ = (char16_t) (((c & 0x1f) << 6) | (c2 & 0x3f)); - continue; - } - if (c < 0xf0) [[likely]] { - if (in + 2 > in_end) [[unlikely]] { - return err_in(); - } - c2 = *in++; - c3 = *in++; - *out++ = (char16_t) (((c & 0x0f) << 12) | - ((c2 & 0x3f) << 6) | (c3 & 0x3f)); - continue; - } else { - if (in + 3 > in_end) [[unlikely]] { - return err_in(); - } - if (c >= 0xf8) [[unlikely]] { - LOGW("Invalid UTF-8 leading byte: 0x%02x", c); - } - // Multiple UTF16 characters with surrogates - c2 = *in++; - c3 = *in++; - c4 = *in++; - w = utf8_4b_to_utf32(c, c2, c3, c4); - if (w < 0x10000) [[unlikely]] { - *out++ = (char16_t) (w); - } else { - if (out + 2 > out_end) [[unlikely]] { - // Ooops.... not enough room for this surrogate pair. - return out; - } - *out++ = (char16_t) (((w - 0x10000) >> 10) + 0xd800); - *out++ = (char16_t) (((w - 0x10000) & 0x3ff) + 0xdc00); - } - continue; - } - } - return out; -} - -} diff --git a/sysbridge/src/main/cpp/android/utils/Unicode.h b/sysbridge/src/main/cpp/android/utils/Unicode.h deleted file mode 100644 index d60d5d6ba6..0000000000 --- a/sysbridge/src/main/cpp/android/utils/Unicode.h +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2005 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -#ifndef ANDROID_UNICODE_H -#define ANDROID_UNICODE_H - -#include -#include - -extern "C" { - -// Standard string functions on char16_t strings. -int strcmp16(const char16_t *, const char16_t *); -int strncmp16(const char16_t *s1, const char16_t *s2, size_t n); -size_t strlen16(const char16_t *); -size_t strnlen16(const char16_t *, size_t); -char16_t *strstr16(const char16_t*, const char16_t*); - -// Version of comparison that supports embedded NULs. -// This is different than strncmp() because we don't stop -// at a nul character and consider the strings to be different -// if the lengths are different (thus we need to supply the -// lengths of both strings). This can also be used when -// your string is not nul-terminated as it will have the -// equivalent result as strcmp16 (unlike strncmp16). -int strzcmp16(const char16_t *s1, size_t n1, const char16_t *s2, size_t n2); - -/** - * Measure the length of a UTF-32 string in UTF-8. If the string is invalid - * such as containing a surrogate character, -1 will be returned. - */ -ssize_t utf32_to_utf8_length(const char32_t *src, size_t src_len); - -/** - * Stores a UTF-8 string converted from "src" in "dst", if "dst_length" is not - * large enough to store the string, the part of the "src" string is stored - * into "dst" as much as possible. See the examples for more detail. - * Returns the size actually used for storing the string. - * dst" is not nul-terminated when dst_len is fully used (like strncpy). - * - * \code - * Example 1 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" >= 7 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84\0 - * (note that "dst" is nul-terminated) - * - * Example 2 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 5 - * -> - * Returned value == 3 - * "dst" becomes \xE3\x81\x82\0 - * (note that "dst" is nul-terminated, but \u3044 is not stored in "dst" - * since "dst" does not have enough size to store the character) - * - * Example 3 - * "src" == \u3042\u3044 (\xE3\x81\x82\xE3\x81\x84) - * "src_len" == 2 - * "dst_len" == 6 - * -> - * Returned value == 6 - * "dst" becomes \xE3\x81\x82\xE3\x81\x84 - * (note that "dst" is NOT nul-terminated, like strncpy) - * \endcode - */ -void utf32_to_utf8(const char32_t* src, size_t src_len, char* dst, size_t dst_len); - -/** - * Returns the unicode value at "index". - * Returns -1 when the index is invalid (equals to or more than "src_len"). - * If returned value is positive, it is able to be converted to char32_t, which - * is unsigned. Then, if "next_index" is not NULL, the next index to be used is - * stored in "next_index". "next_index" can be NULL. - */ -int32_t utf32_from_utf8_at(const char *src, size_t src_len, size_t index, size_t *next_index); - - -/** - * Returns the UTF-8 length of UTF-16 string "src". - */ -ssize_t utf16_to_utf8_length(const char16_t *src, size_t src_len); - -/** - * Converts a UTF-16 string to UTF-8. The destination buffer must be large - * enough to fit the UTF-16 as measured by utf16_to_utf8_length with an added - * NUL terminator. - */ -void utf16_to_utf8(const char16_t* src, size_t src_len, char* dst, size_t dst_len); - -/** - * Returns the UTF-16 length of UTF-8 string "src". Returns -1 in case - * it's invalid utf8. No buffer over-read occurs because of bound checks. Using overreadIsFatal you - * can ask to log a message and fail in case the invalid utf8 could have caused an override if no - * bound checks were used (otherwise -1 is returned). - */ -ssize_t utf8_to_utf16_length(const uint8_t* src, size_t srcLen, bool overreadIsFatal = false); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. - * Returns a pointer to the end of the string (where a NUL terminator might go - * if you wanted to add one). At most dstLen characters are written; it won't emit half a surrogate - * pair. If dstLen == 0 nothing is written and dst is returned. If dstLen > SSIZE_MAX it aborts - * (this being probably a negative number returned as an error and casted to unsigned). - */ -char16_t* utf8_to_utf16_no_null_terminator( - const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); - -/** - * Convert UTF-8 to UTF-16 including surrogate pairs. At most dstLen - 1 - * characters are written; it won't emit half a surrogate pair; and a NUL terminator is appended - * after. dstLen - 1 can be measured beforehand using utf8_to_utf16_length. Aborts if dstLen == 0 - * (at least one character is needed for the NUL terminator) or dstLen > SSIZE_MAX (the latter - * case being likely a negative number returned as an error and casted to unsigned) . Returns a - * pointer to the NUL terminator. - */ -char16_t *utf8_to_utf16( - const uint8_t* src, size_t srcLen, char16_t* dst, size_t dstLen); - -} - -#endif diff --git a/sysbridge/src/main/cpp/libevdev_jni.cpp b/sysbridge/src/main/cpp/libevdev_jni.cpp deleted file mode 100644 index 829d5df81a..0000000000 --- a/sysbridge/src/main/cpp/libevdev_jni.cpp +++ /dev/null @@ -1,707 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "libevdev/libevdev.h" -#include "libevdev/libevdev-uinput.h" -#include "logging.h" -#include "android/input/KeyLayoutMap.h" -#include "android/libbase/result.h" -#include "android/input/InputDevice.h" -#include "aidl/io/github/sds100/keymapper/sysbridge/IEvdevCallback.h" -#include -#include -#include -#include -#include -#include -#include - -using aidl::io::github::sds100::keymapper::sysbridge::IEvdevCallback; - -enum CommandType { - STOP -}; - -struct Command { - CommandType type; -}; - -struct DeviceContext { - struct libevdev *evdev; - struct libevdev_uinput *uinputDev; - struct android::KeyLayoutMap keyLayoutMap; - char devicePath[256]; - int fd; -}; - -static int epollFd = -1; -static std::mutex epollMutex; - -static int commandEventFd = -1; -static std::queue commandQueue; -static std::mutex commandMutex; - -// This maps the file descriptor of an evdev device to its context. -static std::map *evdevDevices = new std::map(); -static std::mutex evdevDevicesMutex; -static std::map *fdToDevicePath = new std::map(); - -#define DEBUG_PROBE false - -jint JNI_OnLoad(JavaVM *vm, void *reserved) { - evdevDevices = new std::map(); - return JNI_VERSION_1_6; -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_grabEvdevDeviceNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath) { - - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - - { - std::lock_guard epollLock(epollMutex); - if (epollFd == -1) { - LOGE("Epoll is not initialized. Cannot grab evdev device."); - return false; - } - - // Lock to prevent concurrent grab/ungrab operations on the same device - std::lock_guard lock(evdevDevicesMutex); - - // Check if device is already grabbed - if (evdevDevices->contains(devicePath)) { - LOGW("Device %s is already grabbed. Maybe it is a virtual uinput device.", - devicePath); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - // Perform synchronous grab operation - struct libevdev *dev = nullptr; - - // MUST be NONBLOCK so that the loop reading the evdev events eventually returns - // due to an EAGAIN error. - int fd = open(devicePath, O_RDONLY | O_NONBLOCK); - if (fd == -1) { - LOGE("Failed to open device %s: %s", devicePath, strerror(errno)); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - int rc = libevdev_new_from_fd(fd, &dev); - if (rc != 0) { - LOGE("Failed to create libevdev device from %s: %s", devicePath, strerror(errno)); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - rc = libevdev_grab(dev, LIBEVDEV_GRAB); - if (rc < 0) { - LOGE("Failed to grab evdev device %s: %s", - libevdev_get_name(dev), strerror(-rc)); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - // Create a dummy InputDeviceIdentifier for key layout loading - android::InputDeviceIdentifier identifier; - identifier.name = std::string(libevdev_get_name(dev)); - identifier.bus = libevdev_get_id_bustype(dev); - identifier.vendor = libevdev_get_id_vendor(dev); - identifier.product = libevdev_get_id_product(dev); - - std::string klPath = android::getInputDeviceConfigurationFilePathByDeviceIdentifier( - identifier, android::InputDeviceConfigurationFileType::KEY_LAYOUT); - - auto klResult = android::KeyLayoutMap::load(klPath, nullptr); - - if (!klResult.ok()) { - LOGE("key layout map not found for device %s", libevdev_get_name(dev)); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - struct libevdev_uinput *uinputDev = nullptr; - int uinputFd = open("/dev/uinput", O_RDWR); - if (uinputFd < 0) { - LOGE("Failed to open /dev/uinput to clone the device."); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - rc = libevdev_uinput_create_from_device(dev, uinputFd, &uinputDev); - - if (rc < 0) { - LOGE("Failed to create uinput device from evdev device %s: %s", - libevdev_get_name(dev), strerror(-rc)); - close(uinputFd); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - DeviceContext context = { - dev, - uinputDev, - *klResult.value(), - {}, // Initialize devicePath array - fd - }; - - strcpy(context.devicePath, devicePath); - - // Already checked at the start of the method whether epoll was running - struct epoll_event event{}; - event.events = EPOLLIN; - event.data.fd = fd; - - if (epoll_ctl(epollFd, EPOLL_CTL_ADD, fd, &event) == -1) { - LOGE("Failed to add new device to epoll: %s", strerror(errno)); - libevdev_uinput_destroy(uinputDev); - libevdev_grab(dev, LIBEVDEV_UNGRAB); - libevdev_free(dev); - close(fd); - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return false; - } - - evdevDevices->insert_or_assign(devicePath, context); - fdToDevicePath->insert_or_assign(fd, devicePath); - result = true; - - LOGI("Grabbed device %s, %s", libevdev_get_name(dev), context.devicePath); - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} - -struct timeval powerButtonDownTime = {0, 0}; - -/** - * @return Whether the events were all handled by the callback. If the callback dies then this - * returns false. - */ -bool onEpollEvdevEvent(DeviceContext *deviceContext, IEvdevCallback *callback) { - struct input_event inputEvent{}; - - int rc = libevdev_next_event(deviceContext->evdev, LIBEVDEV_READ_FLAG_NORMAL, &inputEvent); - - if (rc < 0) { - if (rc != -EAGAIN) { - LOGE("libevdev_next_event failed with error %d: %s", rc, strerror(-rc)); - } - return rc == -EAGAIN; - } - - do { - if (rc == LIBEVDEV_READ_STATUS_SUCCESS) { // rc == 0 - int32_t outKeycode = -1; - uint32_t outFlags = -1; - - deviceContext->keyLayoutMap.mapKey(inputEvent.code, 0, &outKeycode, &outFlags); - - // Kill the system bridge when power button is held down and released after 10+ seconds. - // 26 = KEYCODE_POWER - if (inputEvent.code == KEY_POWER || outKeycode == 26) { - if (inputEvent.value == 1) { - // Down click - powerButtonDownTime = inputEvent.time; - } else if (inputEvent.value == 0) { - // Up click - - // If held down for 10 seconds or more, kill system bridge. - if (inputEvent.time.tv_sec - powerButtonDownTime.tv_sec >= 10) { - callback->onEmergencyKillSystemBridge(); - exit(0); - } - } - } - - bool returnValue; - ndk::ScopedAStatus callbackResult = callback->onEvdevEvent(deviceContext->devicePath, - inputEvent.time.tv_sec, - inputEvent.time.tv_usec, - inputEvent.type, - inputEvent.code, - inputEvent.value, - outKeycode, - &returnValue); - - if (!callbackResult.isOk()) { - return false; - } - - if (!returnValue) { - libevdev_uinput_write_event(deviceContext->uinputDev, - inputEvent.type, - inputEvent.code, - inputEvent.value); - } - - rc = libevdev_next_event(deviceContext->evdev, LIBEVDEV_READ_FLAG_NORMAL, &inputEvent); - - } else if (rc == LIBEVDEV_READ_STATUS_SYNC) { - rc = libevdev_next_event(deviceContext->evdev, - LIBEVDEV_READ_FLAG_NORMAL | LIBEVDEV_READ_FLAG_SYNC, - &inputEvent); - } - - if (rc < 0 && rc != -EAGAIN) { - LOGE("libevdev_next_event failed with error %d: %s", rc, strerror(-rc)); - return false; - } - } while (rc != -EAGAIN); - - return true; -} - -// Set this to some upper limit. It is unlikely that Key Mapper will be polling -// more than a few evdev devices at once. -static int MAX_EPOLL_EVENTS = 100; - -extern "C" -JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_startEvdevEventLoop(JNIEnv *env, - jobject thiz, - jobject jCallbackBinder) { - std::unique_lock epollLock(epollMutex); - - if (epollFd != -1 || commandEventFd != -1) { - LOGE("The evdev event loop has already started."); - return; - } - - epollFd = epoll_create1(EPOLL_CLOEXEC); - if (epollFd == -1) { - LOGE("Failed to create epoll fd: %s", strerror(errno)); - return; - } - - commandEventFd = eventfd(0, EFD_CLOEXEC | EFD_NONBLOCK); - if (commandEventFd == -1) { - LOGE("Failed to create command eventfd: %s", strerror(errno)); - close(epollFd); - return; - } - - struct epoll_event event{}; - event.events = EPOLLIN; - event.data.fd = commandEventFd; - - if (epoll_ctl(epollFd, EPOLL_CTL_ADD, commandEventFd, &event) == -1) { - LOGE("Failed to add command eventfd to epoll: %s", strerror(errno)); - close(epollFd); - close(commandEventFd); - epollLock.unlock(); - return; - } - - epollLock.unlock(); - - AIBinder *callbackAIBinder = AIBinder_fromJavaBinder(env, jCallbackBinder); - const ::ndk::SpAIBinder spBinder(callbackAIBinder); - std::shared_ptr callback = IEvdevCallback::fromBinder(spBinder); - - struct epoll_event events[MAX_EPOLL_EVENTS]; - bool running = true; - - LOGI("Start evdev event loop"); - - ndk::ScopedAStatus callbackResult = callback->onEvdevEventLoopStarted(); - - if (!callbackResult.isOk()) { - LOGE("Callback is dead. Not starting evdev loop."); - return; - } - - while (running) { - int n = epoll_wait(epollFd, events, MAX_EPOLL_EVENTS, -1); - - for (int i = 0; i < n; ++i) { - int fd = events[i].data.fd; - if (fd == commandEventFd) { - uint64_t val; - ssize_t s = read(commandEventFd, &val, sizeof(val)); - - if (s < 0) { - LOGE("Error reading from command event fd: %s", strerror(errno)); - } - - std::vector commandsToProcess; - { - std::lock_guard lock(commandMutex); - while (!commandQueue.empty()) { - commandsToProcess.push_back(commandQueue.front()); - commandQueue.pop(); - } - } - - // Process commands without holding the mutex - for (const auto &cmd: commandsToProcess) { - if (cmd.type == STOP) { - running = false; - break; - } - } - } else { - if ((events[i].events & (EPOLLHUP | EPOLLERR))) { - LOGI("Device disconnected, removing from epoll."); - epoll_ctl(epollFd, EPOLL_CTL_DEL, fd, nullptr); - - std::lock_guard lock(evdevDevicesMutex); - auto it = fdToDevicePath->find(fd); - if (it != fdToDevicePath->end()) { - evdevDevices->erase(it->second); - fdToDevicePath->erase(it); - } - } else { - std::lock_guard lock(evdevDevicesMutex); - auto it = fdToDevicePath->find(fd); - if (it != fdToDevicePath->end()) { - DeviceContext *dc = &evdevDevices->at(it->second); - // If handling the evdev event fails then stop the event loop - // and ungrab all the devices. - bool result = onEpollEvdevEvent(dc, callback.get()); - - if (!result) { - running = false; - break; - } - } - } - } - } - } - - // Cleanup - std::lock_guard lock(evdevDevicesMutex); - - for (auto const &[path, dc]: *evdevDevices) { - libevdev_uinput_destroy(dc.uinputDev); - libevdev_grab(dc.evdev, LIBEVDEV_UNGRAB); - libevdev_free(dc.evdev); - } - - evdevDevices->clear(); - close(commandEventFd); - commandEventFd = -1; - close(epollFd); - epollFd = -1; - - LOGI("Stopped evdev event loop"); -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabEvdevDeviceNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath) { - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - - { - // Lock to prevent concurrent grab/ungrab operations - std::lock_guard lock(evdevDevicesMutex); - - auto it = evdevDevices->find(devicePath); - if (it != evdevDevices->end()) { - // Remove from epoll first (if event loop is running) - if (epollFd != -1) { - if (epoll_ctl(epollFd, EPOLL_CTL_DEL, it->second.fd, nullptr) == -1) { - LOGW("Failed to remove device from epoll: %s", strerror(errno)); - // Continue with ungrab even if epoll removal fails - } - } - - // Do this before freeing the evdev file descriptor - libevdev_uinput_destroy(it->second.uinputDev); - - // Ungrab the device - libevdev_grab(it->second.evdev, LIBEVDEV_UNGRAB); - - // Free resources - libevdev_free(it->second.evdev); - - // Remove from device map - evdevDevices->erase(it); - fdToDevicePath->erase(it->second.fd); - result = true; - - LOGI("Ungrabbed device %s", devicePath); - } - - if (!result) { - LOGW("Device %s was not found in grabbed devices list", devicePath); - } - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} - - -extern "C" -JNIEXPORT void JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_stopEvdevEventLoop(JNIEnv *env, - jobject thiz) { - if (commandEventFd == -1) { - return; - } - - Command cmd = {STOP}; - - std::lock_guard lock(commandMutex); - commandQueue.push(cmd); - - // Notify the event loop - uint64_t val = 1; - ssize_t written = write(commandEventFd, &val, sizeof(val)); - if (written < 0) { - LOGE("Failed to write to commandEventFd: %s", strerror(errno)); - } -} - -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_writeEvdevEventNative(JNIEnv *env, - jobject thiz, - jstring jDevicePath, - jint type, - jint code, - jint value) { - const char *devicePath = env->GetStringUTFChars(jDevicePath, nullptr); - if (devicePath == nullptr) { - return false; - } - - bool result = false; - { - auto it = evdevDevices->find(devicePath); - if (it != evdevDevices->end()) { - int rc = libevdev_uinput_write_event(it->second.uinputDev, type, code, value); - if (rc == 0) { - rc = libevdev_uinput_write_event(it->second.uinputDev, EV_SYN, SYN_REPORT, 0); - } - result = (rc == 0); - } - } - - env->ReleaseStringUTFChars(jDevicePath, devicePath); - return result; -} -extern "C" -JNIEXPORT jboolean JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_ungrabAllEvdevDevicesNative( - JNIEnv *env, - jobject thiz) { - - { - // Lock to prevent concurrent grab/ungrab operations - std::lock_guard lock(evdevDevicesMutex); - std::lock_guard epollLock(epollMutex); - - // Create a copy of the iterator to avoid issues with erasing during iteration - auto devicesCopy = *evdevDevices; - - for (const auto &pair: devicesCopy) { - const DeviceContext &context = pair.second; - - // Remove from epoll first (if event loop is running) - if (epollFd != -1) { - if (epoll_ctl(epollFd, EPOLL_CTL_DEL, context.fd, nullptr) == -1) { - LOGW("Failed to remove device %s from epoll: %s", context.devicePath, - strerror(errno)); - // Continue with ungrab even if epoll removal fails - } - } - - // Do this before freeing the evdev file descriptor - libevdev_uinput_destroy(context.uinputDev); - - // Ungrab the device - libevdev_grab(context.evdev, LIBEVDEV_UNGRAB); - - // Free resources - libevdev_free(context.evdev); - - LOGI("Ungrabbed device %s", context.devicePath); - } - - // Clear all devices from the map - evdevDevices->clear(); - fdToDevicePath->clear(); - } - - return true; -} - -// Helper function to create a Java EvdevDeviceHandle object -jobject -createEvdevDeviceHandle(JNIEnv *env, const char *path, const char *name, int bus, int vendor, - int product) { - // Find the EvdevDeviceHandle class - jclass evdevDeviceHandleClass = env->FindClass( - "io/github/sds100/keymapper/common/models/EvdevDeviceHandle"); - if (evdevDeviceHandleClass == nullptr) { - LOGE("Failed to find EvdevDeviceHandle class"); - return nullptr; - } - - // Get the constructor - jmethodID constructor = env->GetMethodID(evdevDeviceHandleClass, "", - "(Ljava/lang/String;Ljava/lang/String;III)V"); - if (constructor == nullptr) { - LOGE("Failed to find EvdevDeviceHandle constructor"); - return nullptr; - } - - // Create Java strings - jstring jPath = env->NewStringUTF(path); - jstring jName = env->NewStringUTF(name); - - // Create the object - jobject evdevDeviceHandle = env->NewObject(evdevDeviceHandleClass, constructor, jPath, jName, - bus, vendor, product); - - // Clean up local references - env->DeleteLocalRef(jPath); - env->DeleteLocalRef(jName); - env->DeleteLocalRef(evdevDeviceHandleClass); - - return evdevDeviceHandle; -} - -extern "C" -JNIEXPORT jobjectArray JNICALL -Java_io_github_sds100_keymapper_sysbridge_service_SystemBridge_getEvdevDevicesNative(JNIEnv *env, - jobject thiz) { - DIR *dir = opendir("/dev/input"); - - if (dir == nullptr) { - LOGE("Failed to open /dev/input directory"); - return nullptr; - } - - std::vector deviceHandles; - struct dirent *entry; - - std::lock_guard lock(evdevDevicesMutex); - while ((entry = readdir(dir)) != nullptr) { - // Skip . and .. entries - if (strcmp(entry->d_name, ".") == 0 || strcmp(entry->d_name, "..") == 0) { - continue; - } - - char fullPath[256]; - snprintf(fullPath, sizeof(fullPath), "/dev/input/%s", entry->d_name); - - bool ignoreDevice = false; - - { - // Ignore this device if it is a uinput device we created - for (const auto &pair: *evdevDevices) { - DeviceContext context = pair.second; - const char *uinputDevicePath = libevdev_uinput_get_devnode(context.uinputDev); - - if (strcmp(fullPath, uinputDevicePath) == 0) { - LOGW("Ignoring uinput device %s.", uinputDevicePath); - ignoreDevice = true; - break; - } - } - } - - if (ignoreDevice) { - continue; - } - - int fd = open(fullPath, O_RDONLY); - - if (fd == -1) { - continue; - } - - struct libevdev *dev = nullptr; - int status = libevdev_new_from_fd(fd, &dev); - - if (status != 0) { - LOGE("Failed to open libevdev device from path %s: %s", fullPath, strerror(errno)); - close(fd); - continue; - } - - const char *devName = libevdev_get_name(dev); - int devVendor = libevdev_get_id_vendor(dev); - int devProduct = libevdev_get_id_product(dev); - int devBus = libevdev_get_id_bustype(dev); - - if (DEBUG_PROBE) { - LOGD("Evdev device: %s, bus: %d, vendor: %d, product: %d, path: %s", - devName, devBus, devVendor, devProduct, fullPath); - } - - // Create EvdevDeviceHandle object - jobject deviceHandle = createEvdevDeviceHandle(env, fullPath, devName, devBus, devVendor, - devProduct); - if (deviceHandle != nullptr) { - deviceHandles.push_back(deviceHandle); - } - - libevdev_free(dev); - close(fd); - } - - closedir(dir); - - // Create the Java array - jclass evdevDeviceHandleClass = env->FindClass( - "io/github/sds100/keymapper/common/models/EvdevDeviceHandle"); - if (evdevDeviceHandleClass == nullptr) { - LOGE("Failed to find EvdevDeviceHandle class for array creation"); - return nullptr; - } - - jobjectArray result = env->NewObjectArray(deviceHandles.size(), evdevDeviceHandleClass, - nullptr); - if (result == nullptr) { - LOGE("Failed to create EvdevDeviceHandle array"); - env->DeleteLocalRef(evdevDeviceHandleClass); - return nullptr; - } - - // Fill the array - for (size_t i = 0; i < deviceHandles.size(); i++) { - env->SetObjectArrayElement(result, i, deviceHandles[i]); - env->DeleteLocalRef(deviceHandles[i]); // Clean up local reference - } - - env->DeleteLocalRef(evdevDeviceHandleClass); - return result; -} \ No newline at end of file diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbClient.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbClient.kt index 523668d790..7af0397805 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbClient.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbClient.kt @@ -17,6 +17,7 @@ import io.github.sds100.keymapper.sysbridge.adb.AdbProtocol.A_WRTE import java.io.Closeable import java.io.DataInputStream import java.io.DataOutputStream +import java.io.EOFException import java.net.ConnectException import java.net.Socket import java.net.SocketException @@ -105,6 +106,9 @@ internal class AdbClient(private val host: String, private val port: Int, privat return AdbError.SslHandshakeError } catch (e: SocketException) { return AdbError.ConnectionError + } catch (_: EOFException) { + Timber.e("AdbClient.connect(): Caught EOF Exception when reading ADB message") + return AdbError.ConnectionError } return Success(Unit) @@ -114,35 +118,39 @@ internal class AdbClient(private val host: String, private val port: Int, privat val localId = 1 write(A_OPEN, localId, 0, "shell:$command") - var message = read() - when (message.command) { - A_OKAY -> { - while (true) { - message = read() - - val remoteId = message.arg0 - if (message.command == A_WRTE) { - if (message.data_length > 0) { - listener(message.data!!) + try { + var message = read() + when (message.command) { + A_OKAY -> { + while (true) { + message = read() + + val remoteId = message.arg0 + if (message.command == A_WRTE) { + if (message.data_length > 0) { + listener(message.data!!) + } + write(A_OKAY, localId, remoteId) + } else if (message.command == A_CLSE) { + write(A_CLSE, localId, remoteId) + break + } else { + error("not A_WRTE or A_CLSE") } - write(A_OKAY, localId, remoteId) - } else if (message.command == A_CLSE) { - write(A_CLSE, localId, remoteId) - break - } else { - error("not A_WRTE or A_CLSE") } } - } - A_CLSE -> { - val remoteId = message.arg0 - write(A_CLSE, localId, remoteId) - } + A_CLSE -> { + val remoteId = message.arg0 + write(A_CLSE, localId, remoteId) + } - else -> { - error("not A_OKAY or A_CLSE") + else -> { + error("not A_OKAY or A_CLSE") + } } + } catch (_: EOFException) { + Timber.e("AdbClient.shellCommand(): Caught EOF Exception when reading ADB message") } } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbMdns.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbMdns.kt index dde3da9a28..e63621a0bd 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbMdns.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/adb/AdbMdns.kt @@ -96,7 +96,7 @@ internal class AdbMdns(ctx: Context, private val serviceType: AdbServiceType) { ) // You can only resolve one service at a time and they can take some time to resolve. - serviceDiscoveredChannel?.trySend(serviceInfo) + serviceDiscoveredChannel?.trySendBlocking(serviceInfo) } override fun onServiceLost(serviceInfo: NsdServiceInfo) { diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/ktx/Log.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/ktx/Log.kt deleted file mode 100644 index 43c2b6d251..0000000000 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/ktx/Log.kt +++ /dev/null @@ -1,34 +0,0 @@ -@file:Suppress("NOTHING_TO_INLINE") - -package io.github.sds100.keymapper.sysbridge.ktx - -import android.util.Log - -inline val T.TAG: String - get() = - T::class.java.simpleName.let { - if (it.isBlank()) throw IllegalStateException("tag is empty") - if (it.length > 23) it.substring(0, 23) else it - } - -inline fun T.logv(message: String, throwable: Throwable? = null) = - logv(TAG, message, throwable) -inline fun T.logi(message: String, throwable: Throwable? = null) = - logi(TAG, message, throwable) -inline fun T.logw(message: String, throwable: Throwable? = null) = - logw(TAG, message, throwable) -inline fun T.logd(message: String, throwable: Throwable? = null) = - logd(TAG, message, throwable) -inline fun T.loge(message: String, throwable: Throwable? = null) = - loge(TAG, message, throwable) - -inline fun T.logv(tag: String, message: String, throwable: Throwable? = null) = - Log.v(tag, message, throwable) -inline fun T.logi(tag: String, message: String, throwable: Throwable? = null) = - Log.i(tag, message, throwable) -inline fun T.logw(tag: String, message: String, throwable: Throwable? = null) = - Log.w(tag, message, throwable) -inline fun T.logd(tag: String, message: String, throwable: Throwable? = null) = - Log.d(tag, message, throwable) -inline fun T.loge(tag: String, message: String, throwable: Throwable? = null) = - Log.e(tag, message, throwable) diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt index 56629c0d9a..7e1a6dc013 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionManager.kt @@ -17,22 +17,26 @@ import androidx.core.content.ContextCompat import dagger.hilt.android.qualifiers.ApplicationContext import io.github.sds100.keymapper.common.BuildConfigProvider import io.github.sds100.keymapper.common.models.isSuccess +import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.SettingsUtils import io.github.sds100.keymapper.common.utils.Success +import io.github.sds100.keymapper.common.utils.firstBlocking import io.github.sds100.keymapper.common.utils.onFailure +import io.github.sds100.keymapper.data.Keys +import io.github.sds100.keymapper.data.repositories.PreferenceRepository import io.github.sds100.keymapper.sysbridge.ISystemBridge -import io.github.sds100.keymapper.sysbridge.ktx.TAG import io.github.sds100.keymapper.sysbridge.starter.SystemBridgeStarter import io.github.sds100.keymapper.sysbridge.utils.SystemBridgeError import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.StateFlow +import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import timber.log.Timber @@ -44,10 +48,15 @@ import timber.log.Timber class SystemBridgeConnectionManagerImpl @Inject constructor( @ApplicationContext private val ctx: Context, private val coroutineScope: CoroutineScope, + private val preferences: PreferenceRepository, private val starter: SystemBridgeStarter, private val buildConfigProvider: BuildConfigProvider, ) : SystemBridgeConnectionManager { + companion object { + private const val TAG = "SystemBridgeConnectionManagerImpl" + } + private val systemBridgeLock: Any = Any() private var systemBridgeFlow: MutableStateFlow = MutableStateFlow(null) @@ -55,21 +64,27 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( MutableStateFlow( SystemBridgeConnectionState.Disconnected( time = SystemClock.elapsedRealtime(), - isExpected = true, + // Get whether the user previously stopped the system bridge. + isStoppedByUser = + preferences.get(Keys.isSystemBridgeStoppedByUser).firstBlocking() ?: false, ), ) private var isExpectedDeath: Boolean = false private val deathRecipient: DeathRecipient = DeathRecipient { synchronized(systemBridgeLock) { - Timber.e("System Bridge has died") + if (isExpectedDeath) { + Timber.w("System bridge killed by user.") + } else { + Timber.e("System Bridge has died") + } systemBridgeFlow.update { null } connectionState.update { SystemBridgeConnectionState.Disconnected( time = SystemClock.elapsedRealtime(), - isExpected = isExpectedDeath, + isStoppedByUser = isExpectedDeath, ) } @@ -77,8 +92,6 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } } - private var startJob: Job? = null - fun pingBinder(): Boolean { synchronized(systemBridgeLock) { return systemBridgeFlow.value?.asBinder()?.pingBinder() == true @@ -144,25 +157,27 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( @SuppressLint("LogNotTimber") private suspend fun restartSystemBridge(systemBridge: ISystemBridge) { - starter.startSystemBridge(executeCommand = { command -> - try { - val result = systemBridge.executeCommand(command, 10000L)!! - if (result.isSuccess()) { - Success(result.stdout) - } else { - KMError.Exception( - Exception( - "Command failed with exit code ${result.exitCode}: ${result.stdout}", - ), - ) + starter.startSystemBridgeWithLock( + commandExecutor = { command -> + try { + val result = systemBridge.executeCommand(command, 10000L)!! + if (result.isSuccess()) { + Success(result.stdout) + } else { + KMError.Exception( + Exception( + "Command failed with exit code ${result.exitCode}: ${result.stdout}", + ), + ) + } + } catch (_: DeadObjectException) { + // This exception is expected since it is killing the system bridge + Success("") + } catch (e: Exception) { + KMError.Exception(e) } - } catch (_: DeadObjectException) { - // This exception is expected since it is killing the system bridge - Success("") - } catch (e: Exception) { - KMError.Exception(e) - } - }).onFailure { error -> + }, + ).onFailure { error -> Log.e(TAG, "Failed to restart System Bridge: $error") } } @@ -192,15 +207,8 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } @RequiresApi(Build.VERSION_CODES.R) - override fun startWithAdb() { - if (startJob?.isActive == true) { - Timber.i("System Bridge is already starting") - return - } - - startJob = coroutineScope.launch { - starter.startWithAdb() - } + override suspend fun startWithAdb() { + starter.startWithAdb() } private fun preventSystemBridgeKilling(systemBridge: ISystemBridge) { @@ -239,15 +247,8 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } } - override fun startWithRoot() { - if (startJob?.isActive == true) { - Timber.i("System Bridge is already starting") - return - } - - startJob = coroutineScope.launch { - starter.startWithRoot() - } + override suspend fun startWithRoot() { + starter.startWithRoot() } override fun startWithShizuku() { @@ -256,7 +257,7 @@ class SystemBridgeConnectionManagerImpl @Inject constructor( } @SuppressLint("ObsoleteSdkInt") -@RequiresApi(Build.VERSION_CODES.Q) +@RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) interface SystemBridgeConnectionManager { val connectionState: StateFlow @@ -264,11 +265,17 @@ interface SystemBridgeConnectionManager { fun stopSystemBridge() fun restartSystemBridge() - fun startWithRoot() + suspend fun startWithRoot() fun startWithShizuku() - fun startWithAdb() + suspend fun startWithAdb() } fun SystemBridgeConnectionManager.isConnected(): Boolean { return connectionState.value is SystemBridgeConnectionState.Connected } + +suspend fun SystemBridgeConnectionManager.awaitConnected() { + connectionState + .filterIsInstance() + .first() +} diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionState.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionState.kt index 1d4b531783..fea1da57cf 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionState.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/manager/SystemBridgeConnectionState.kt @@ -11,9 +11,9 @@ sealed class SystemBridgeConnectionState { data class Disconnected( override val time: Long, /** - * Whether the disconnection was expected. E.g the user stopped it or the app is - * opening for the first time + * Whether the user previously stopped the service and that is why it is disconnected. + * Once the user manually starts it again, this is set to false. */ - val isExpected: Boolean, + val isStoppedByUser: Boolean, ) : SystemBridgeConnectionState() } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt index ba92a552d4..edfbbe4ab6 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridge.kt @@ -40,10 +40,13 @@ import android.util.Log import android.view.InputEvent import androidx.annotation.RequiresApi import com.android.internal.telephony.ITelephony -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo +import io.github.sds100.keymapper.common.models.GrabTargetKeyCode +import io.github.sds100.keymapper.common.models.GrabbedDeviceHandle import io.github.sds100.keymapper.common.models.ShellResult import io.github.sds100.keymapper.common.utils.UserHandleUtils -import io.github.sds100.keymapper.sysbridge.IEvdevCallback +import io.github.sds100.keymapper.evdev.IEvdevCallback +import io.github.sds100.keymapper.sysbridge.ILogCallback import io.github.sds100.keymapper.sysbridge.ISystemBridge import io.github.sds100.keymapper.sysbridge.provider.BinderContainer import io.github.sds100.keymapper.sysbridge.provider.SystemBridgeBinderProvider @@ -63,23 +66,106 @@ import rikka.hidden.compat.UserManagerApis import rikka.hidden.compat.adapter.ProcessObserverAdapter @SuppressLint("LogNotTimber") -internal class SystemBridge : ISystemBridge.Stub() { +class SystemBridge : ISystemBridge.Stub() { - external fun grabEvdevDeviceNative(devicePath: String): Boolean + @Suppress("KotlinJniMissingFunction") + external fun setGrabTargetsNative( + devices: Array, + ): Array - external fun ungrabEvdevDeviceNative(devicePath: String): Boolean - external fun ungrabAllEvdevDevicesNative(): Boolean - external fun writeEvdevEventNative( - devicePath: String, + @Suppress("KotlinJniMissingFunction") + external fun writeEvdevEventNative(deviceId: Int, type: Int, code: Int, value: Int): Boolean + + @Suppress("KotlinJniMissingFunction") + external fun writeEvdevEventKeyCodeNative(deviceId: Int, keyCode: Int, value: Int): Boolean + + @Suppress("KotlinJniMissingFunction") + external fun getEvdevDevicesNative(): Array + + @Suppress("KotlinJniMissingFunction") + external fun initEvdevManager() + + @Suppress("KotlinJniMissingFunction") + external fun destroyEvdevManager() + + @Suppress("KotlinJniMissingFunction") + external fun setLogLevelNative(level: Int) + + /** + * Called from Rust via JNI when an evdev event occurs. + * Forwards the call to the registered IEvdevCallback and returns whether the event was consumed. + */ + @Suppress("unused") + fun onEvdevEvent( + deviceId: Int, + timeSec: Long, + timeUsec: Long, type: Int, code: Int, value: Int, - ): Boolean + androidCode: Int, + ): Boolean { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return false + return try { + callback.onEvdevEvent(deviceId, timeSec, timeUsec, type, code, value, androidCode) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + false + } + } + } + + @Suppress("unused") + fun onGrabbedDevicesChanged(devices: Array) { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return + try { + callback.onGrabbedDevicesChanged(devices) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + } + } + } + + @Suppress("unused") + fun onEvdevDevicesChanged(devices: Array) { + synchronized(evdevCallbackLock) { + val callback = evdevCallback ?: return + try { + callback.onEvdevDevicesChanged(devices) + } catch (e: Exception) { + Log.e(TAG, "Error calling evdev callback", e) + } + } + } - external fun getEvdevDevicesNative(): Array + /** + * Called from Rust via JNI when the power button is held for 10+ seconds. + * Forwards the call to the registered IEvdevCallback for emergency system bridge kill. + */ + @Suppress("unused") + fun onEmergencyKillSystemBridge() { + synchronized(evdevCallbackLock) { + evdevCallback?.onEmergencyKillSystemBridge() + } + } - external fun startEvdevEventLoop(callback: IBinder) - external fun stopEvdevEventLoop() + /** + * Called from Rust via JNI when a log message is emitted. + * Forwards the call to the registered ILogCallback. + */ + @Suppress("unused") + fun onLogMessage(level: Int, message: String) { + synchronized(logCallbackLock) { + val callback = logCallback ?: return + try { + callback.onLog(level, message) + } catch (e: Exception) { + Log.e(TAG, "Error calling log callback", e) + } + } + } companion object { private const val TAG: String = "KeyMapperSystemBridge" @@ -167,17 +253,24 @@ internal class SystemBridge : ISystemBridge.Stub() { private val evdevCallbackLock: Any = Any() private var evdevCallback: IEvdevCallback? = null private val evdevCallbackDeathRecipient: IBinder.DeathRecipient = IBinder.DeathRecipient { - Log.i(TAG, "EvdevCallback binder died") - evdevCallback = null + Log.i(TAG, "EvdevCallback binder died. Stopping evdev event loop") - coroutineScope.launch(Dispatchers.Default) { - stopEvdevEventLoop() + synchronized(evdevCallbackLock) { + evdevCallback = null } // Start periodic check for Key Mapper installation startKeyMapperPeriodicCheck() } + private val logCallbackLock: Any = Any() + private var logCallback: ILogCallback? = null + private val logCallbackDeathRecipient: IBinder.DeathRecipient = IBinder.DeathRecipient { + synchronized(logCallbackLock) { + logCallback = null + } + } + private val inputManager: IInputManager private val wifiManager: IWifiManager? private val permissionManager: IPermissionManager @@ -206,7 +299,7 @@ internal class SystemBridge : ISystemBridge.Stub() { val libraryPath = System.getProperty("keymapper_sysbridge.library.path") @SuppressLint("UnsafeDynamicallyLoadedCode") - System.load("$libraryPath/libevdev.so") + System.load("$libraryPath/libevdev_manager.so") Log.i(TAG, "SystemBridge starting... Version code $versionCode") @@ -301,6 +394,8 @@ internal class SystemBridge : ISystemBridge.Stub() { sendBinderToApp() } + initEvdevManager() + Log.i(TAG, "SystemBridge started complete. Version code $versionCode") } @@ -345,6 +440,11 @@ internal class SystemBridge : ISystemBridge.Stub() { } override fun destroy() { + Log.i(TAG, "Destroying system bridge...") + + stopKeyMapperPeriodicCheck() + destroyEvdevManager() + Log.i(TAG, "SystemBridge destroyed") // Must be last line in this method because it halts the JVM. @@ -369,54 +469,26 @@ internal class SystemBridge : ISystemBridge.Stub() { this.evdevCallback = callback binder.linkToDeath(evdevCallbackDeathRecipient, 0) } - - coroutineScope.launch(Dispatchers.IO) { - mainHandler.post { - startEvdevEventLoop(binder) - } - } } override fun unregisterEvdevCallback() { synchronized(evdevCallbackLock) { evdevCallback?.asBinder()?.unlinkToDeath(evdevCallbackDeathRecipient, 0) evdevCallback = null - stopEvdevEventLoop() - } - } - - override fun grabEvdevDevice(devicePath: String?): Boolean { - devicePath ?: return false - return grabEvdevDeviceNative(devicePath) - } - - override fun grabEvdevDeviceArray(devicePath: Array?): Boolean { - devicePath ?: return false - - for (path in devicePath) { - Log.i(TAG, "Grabbing evdev device $path") - grabEvdevDeviceNative(path) } - - return true - } - - override fun ungrabEvdevDevice(devicePath: String?): Boolean { - devicePath ?: return false - ungrabEvdevDeviceNative(devicePath) - return true } - override fun ungrabAllEvdevDevices(): Boolean { - ungrabAllEvdevDevicesNative() - return true + override fun setGrabTargets( + devices: Array?, + ): Array? { + return setGrabTargetsNative(devices?.filterNotNull()?.toTypedArray() ?: emptyArray()) } override fun injectInputEvent(event: InputEvent?, mode: Int): Boolean { return inputManager.injectInputEvent(event, mode) } - override fun getEvdevInputDevices(): Array? { + override fun getEvdevInputDevices(): Array { return getEvdevDevicesNative() } @@ -428,15 +500,19 @@ internal class SystemBridge : ISystemBridge.Stub() { return wifiManager.setWifiEnabled(processPackageName, enable) } - override fun writeEvdevEvent(devicePath: String?, type: Int, code: Int, value: Int): Boolean { - devicePath ?: return false - return writeEvdevEventNative(devicePath, type, code, value) + override fun writeEvdevEvent(deviceId: Int, type: Int, code: Int, value: Int): Boolean { + return writeEvdevEventNative(deviceId, type, code, value) + } + + override fun writeEvdevEventKeyCode(deviceId: Int, keyCode: Int, value: Int): Boolean { + return writeEvdevEventKeyCodeNative(deviceId, keyCode, value) } override fun getProcessUid(): Int { return Process.myUid() } + @RequiresApi(Build.VERSION_CODES.R) override fun grantPermission(permission: String?, deviceId: Int) { val userId = UserHandleUtils.getCallingUserId() @@ -733,11 +809,14 @@ internal class SystemBridge : ISystemBridge.Stub() { tetheringConnector.registerTetheringEventCallback(callback, processPackageName) // Wait for callback with timeout using Handler - mainHandler.postDelayed({ - synchronized(lock) { - lock.notify() - } - }, timeoutMillis) + mainHandler.postDelayed( + { + synchronized(lock) { + lock.notify() + } + }, + timeoutMillis, + ) synchronized(lock) { lock.wait(timeoutMillis) @@ -782,4 +861,32 @@ internal class SystemBridge : ISystemBridge.Stub() { -1 } } + + override fun registerLogCallback(callback: ILogCallback?) { + callback ?: return + + Log.i(TAG, "Register log callback") + + val binder = callback.asBinder() + + if (this.logCallback != null) { + unregisterLogCallback() + } + + synchronized(logCallbackLock) { + this.logCallback = callback + binder.linkToDeath(logCallbackDeathRecipient, 0) + } + } + + override fun unregisterLogCallback() { + synchronized(logCallbackLock) { + logCallback?.asBinder()?.unlinkToDeath(logCallbackDeathRecipient, 0) + logCallback = null + } + } + + override fun setLogLevel(level: Int) { + setLogLevelNative(level) + } } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt index 7a96446c2d..5c46b23560 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/service/SystemBridgeSetupController.kt @@ -12,7 +12,6 @@ import android.os.Build import android.provider.Settings import android.service.quicksettings.TileService import androidx.annotation.RequiresApi -import androidx.annotation.RequiresPermission import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext @@ -25,6 +24,7 @@ import io.github.sds100.keymapper.common.utils.onSuccess import io.github.sds100.keymapper.sysbridge.adb.AdbManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionManager import io.github.sds100.keymapper.sysbridge.manager.SystemBridgeConnectionState +import io.github.sds100.keymapper.sysbridge.manager.awaitConnected import javax.inject.Inject import javax.inject.Singleton import kotlinx.coroutines.CoroutineScope @@ -34,7 +34,7 @@ import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.filterIsInstance +import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.filterNotNull import kotlinx.coroutines.flow.first import kotlinx.coroutines.flow.stateIn @@ -60,6 +60,10 @@ class SystemBridgeSetupControllerImpl @Inject constructor( private val activityManager: ActivityManager by lazy { ctx.getSystemService()!! } + private val _isStarting: MutableStateFlow = MutableStateFlow(false) + override val isStarting: StateFlow = _isStarting + private var startJob: Job? = null + override val isDeveloperOptionsEnabled: MutableStateFlow = MutableStateFlow(getDeveloperOptionsEnabled()) @@ -74,8 +78,6 @@ class SystemBridgeSetupControllerImpl @Inject constructor( private val isAdbPairedResult: MutableStateFlow = MutableStateFlow(null) private var isAdbPairedJob: Job? = null - private var autoStartJob: Job? = null - init { // Automatically go back to the Key Mapper app when turning on wireless debugging coroutineScope.launch { @@ -104,13 +106,65 @@ class SystemBridgeSetupControllerImpl @Inject constructor( } override fun startWithRoot() { - coroutineScope.launch { - connectionManager.startWithRoot() + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithRoot() + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } } } override fun startWithShizuku() { - connectionManager.startWithShizuku() + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithShizuku() + + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } + } + } + + @RequiresApi(Build.VERSION_CODES.R) + override fun startWithAdb() { + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + + startJob = coroutineScope.launch { + _isStarting.value = true + try { + connectionManager.startWithAdb() + // Wait for the service to bind and start system bridge + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } + } finally { + _isStarting.value = false + } + } } /** @@ -119,65 +173,68 @@ class SystemBridgeSetupControllerImpl @Inject constructor( */ @RequiresApi(Build.VERSION_CODES.R) override fun autoStartWithAdb() { - autoStartJob?.cancel() - - autoStartJob = coroutineScope.launch { - if (!canWriteGlobalSettings()) { - Timber.w("Cannot auto start with ADB. WRITE_SECURE_SETTINGS permission not granted") - return@launch - } - - if (connectionManager.connectionState.value - !is SystemBridgeConnectionState.Disconnected - ) { - Timber.w("Not auto starting. System Bridge is already connected.") - return@launch - } - - SettingsUtils.putGlobalSetting(ctx, DEVELOPER_OPTIONS_SETTING, 1) + if (startJob?.isActive == true) { + Timber.i("System Bridge is already starting") + return + } + startJob = coroutineScope.launch { + _isStarting.value = true try { - withTimeout(5000L) { isDeveloperOptionsEnabled.first { it } } - } catch (_: TimeoutCancellationException) { - return@launch - } + if (!canWriteGlobalSettings()) { + Timber.w( + "Cannot auto start with ADB. WRITE_SECURE_SETTINGS permission not granted", + ) + return@launch + } - if (isAdbPaired()) { - // This is IMPORTANT. First turn on ADB before enabling wireless debugging because - // turning on developer options just before can cause the Shell to be killed once - // the system bridge is started. - SettingsUtils.putGlobalSetting(ctx, Settings.Global.ADB_ENABLED, 1) - SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) + if (connectionManager.connectionState.value + !is SystemBridgeConnectionState.Disconnected + ) { + Timber.w("Not auto starting. System Bridge is already connected.") + return@launch + } + + SettingsUtils.putGlobalSetting(ctx, DEVELOPER_OPTIONS_SETTING, 1) - // Wait for wireless debugging to be enabled before starting with ADB try { - withTimeout(5000L) { isWirelessDebuggingEnabled.first { it } } + withTimeout(5000L) { isDeveloperOptionsEnabled.first { it } } } catch (_: TimeoutCancellationException) { return@launch } - startWithAdb() + if (isAdbPaired()) { + // This is IMPORTANT. First turn on ADB before enabling wireless debugging because + // turning on developer options just before can cause the Shell to be killed once + // the system bridge is started. + SettingsUtils.putGlobalSetting(ctx, Settings.Global.ADB_ENABLED, 1) + SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) - // Wait for the service to connect before turning off wireless debugging - withTimeoutOrNull(5000L) { - connectionManager.connectionState - .filterIsInstance() - .first() - } + // Wait for wireless debugging to be enabled before starting with ADB + try { + withTimeout(5000L) { isWirelessDebuggingEnabled.first { it } } + } catch (_: TimeoutCancellationException) { + return@launch + } + + connectionManager.startWithAdb() + + // Wait for the service to connect before turning off wireless debugging + withTimeoutOrNull(10000L) { + connectionManager.awaitConnected() + } - // Disable wireless debugging when done - SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 0) - } else { - Timber.e("Autostart failed. ADB not paired successfully.") + // Disable wireless debugging when done + SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 0) + } else { + Timber.e("Autostart failed. ADB not paired successfully.") + } + } finally { + _isStarting.value = false } } } - @RequiresApi(Build.VERSION_CODES.R) - override fun startWithAdb() { - connectionManager.startWithAdb() - } - @RequiresApi(Build.VERSION_CODES.R) override fun launchPairingAssistant() { launchWirelessDebuggingActivity() @@ -235,9 +292,7 @@ class SystemBridgeSetupControllerImpl @Inject constructor( isAdbPairedResult.value = null isAdbPairedJob = coroutineScope.launch { - if (!getWirelessDebuggingEnabled()) { - SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) - } + SettingsUtils.putGlobalSetting(ctx, ADB_WIRELESS_SETTING, 1) // Try running a command to see if the pairing is working correctly. // This will execute the "exit" command in the shell so it immediately closes @@ -343,6 +398,7 @@ class SystemBridgeSetupControllerImpl @Inject constructor( @RequiresApi(Constants.SYSTEM_BRIDGE_MIN_API) interface SystemBridgeSetupController { val setupAssistantStep: Flow + val isStarting: StateFlow val isDeveloperOptionsEnabled: Flow fun enableDeveloperOptions() @@ -361,7 +417,5 @@ interface SystemBridgeSetupController { fun startWithRoot() fun startWithShizuku() fun startWithAdb() - - @RequiresPermission(Manifest.permission.WRITE_SECURE_SETTINGS) fun autoStartWithAdb() } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt index 360f0e1c6a..317b87ecbd 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/starter/SystemBridgeStarter.kt @@ -23,7 +23,6 @@ import io.github.sds100.keymapper.sysbridge.BuildConfig import io.github.sds100.keymapper.sysbridge.IShizukuStarterService import io.github.sds100.keymapper.sysbridge.R import io.github.sds100.keymapper.sysbridge.adb.AdbManager -import io.github.sds100.keymapper.sysbridge.ktx.loge import io.github.sds100.keymapper.sysbridge.shizuku.ShizukuStarterService import java.io.BufferedReader import java.io.DataInputStream @@ -75,15 +74,17 @@ class SystemBridgeStarter @Inject constructor( Timber.i("Starting System Bridge with Shizuku starter service") try { runBlocking { - startSystemBridge(executeCommand = { command -> - val output = service.executeCommand(command) - - if (output == null) { - KMError.UnknownIOError - } else { - Success(output) - } - }) + startSystemBridgeWithLock( + commandExecutor = { command -> + val output = service.executeCommand(command) + + if (output == null) { + KMError.UnknownIOError + } else { + Success(output) + } + }, + ) } } catch (e: RemoteException) { Timber.e("Exception starting with Shizuku starter service: $e") @@ -135,7 +136,7 @@ class SystemBridgeStarter @Inject constructor( return KMError.Exception(IllegalStateException("User is locked")) } - return startSystemBridge(executeCommand = adbManager::executeCommand) + return startSystemBridgeWithLock(commandExecutor = adbManager::executeCommand) .onFailure { error -> Timber.e("Failed to start system bridge with ADB: $error") } @@ -148,66 +149,74 @@ class SystemBridgeStarter @Inject constructor( } Timber.i("Starting System Bridge with root") - startSystemBridge(executeCommand = { command -> - val output = withContext(Dispatchers.IO) { - Shell.cmd(command).exec() - } + startSystemBridgeWithLock( + commandExecutor = { command -> + val output = withContext(Dispatchers.IO) { + Shell.cmd(command).exec() + } - if (output.isSuccess) { - Success(output.out.plus(output.err).joinToString("\n")) - } else { - KMError.UnknownIOError - } - }) + if (output.isSuccess) { + Success(output.out.plus(output.err).joinToString("\n")) + } else { + KMError.UnknownIOError + } + }, + ) } - suspend fun startSystemBridge( - executeCommand: suspend (String) -> KMResult, + suspend fun startSystemBridgeWithLock( + commandExecutor: suspend (String) -> KMResult, ): KMResult { startMutex.withLock { - val externalFilesParent = try { - ctx.getExternalFilesDir(null)?.parentFile - } catch (e: IOException) { - return KMError.UnknownIOError - } + return startSystemBridge(commandExecutor) + } + } + + private suspend fun startSystemBridge( + commandExecutor: suspend (String) -> KMResult, + ): KMResult { + val externalFilesParent = try { + ctx.getExternalFilesDir(null)?.parentFile + } catch (e: IOException) { + return KMError.UnknownIOError + } - Timber.i("Copy starter files to ${externalFilesParent?.absolutePath}") + Timber.i("Copy starter files to ${externalFilesParent?.absolutePath}") - val outputStarterBinary = File(externalFilesParent, "starter") - val outputStarterScript = File(externalFilesParent, "start.sh") + val outputStarterBinary = File(externalFilesParent, "starter") + val outputStarterScript = File(externalFilesParent, "start.sh") - val copyFilesResult = withContext(Dispatchers.IO) { - copyNativeLibrary(outputStarterBinary).then { - // Create the start.sh shell script - writeStarterScript( - outputStarterScript, - outputStarterBinary.absolutePath, - ) - Success(Unit) - } + val copyFilesResult = withContext(Dispatchers.IO) { + copyNativeLibrary(outputStarterBinary).then { + // Create the start.sh shell script + writeStarterScript( + outputStarterScript, + outputStarterBinary.absolutePath, + ) + Success(Unit) } + } - val startCommand = - "sh ${outputStarterScript.absolutePath} --apk=$baseApkPath --lib=$libPath --package=$packageName --version=${buildConfigProvider.versionCode}" - - return copyFilesResult - .then { executeCommand(startCommand) } - .then { output -> - // Adb on Android 11 has no permission to access Android/data so use /data/user_de. - if (output.contains( - "/Android/data/${ctx.packageName}/start.sh: Permission denied", - ) - ) { - Timber.w( - "ADB has no permission to access Android/data/${ctx.packageName}/start.sh. Trying to use /data/user_de instead...", - ) - - startSystemBridgeFromProtectedStorage(executeCommand) - } else { - Success(output) - } + val startCommand = + "sh ${outputStarterScript.absolutePath} --apk=$baseApkPath --lib=$libPath --package=$packageName --version=${buildConfigProvider.versionCode}" + + return copyFilesResult + .then { commandExecutor(startCommand) } + .then { output -> + // Adb on Android 11 has no permission to access Android/data so use /data/user_de. + if (output.contains( + "/Android/data/${ctx.packageName}/start.sh: Permission denied", + ) + ) { + Timber.w( + "ADB has no permission to access Android/data/${ctx.packageName}/start.sh. Trying to use /data/user_de instead...", + ) + + startSystemBridgeFromProtectedStorage(commandExecutor) + } else { + Success(output) } - } + } } private suspend fun startSystemBridgeFromProtectedStorage( @@ -261,7 +270,7 @@ class SystemBridgeStarter @Inject constructor( return executeCommand(startCommand) } catch (e: IOException) { - loge("write files", e) + Timber.e(e) return KMError.UnknownIOError } } diff --git a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/utils/SystemBridgeError.kt b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/utils/SystemBridgeError.kt index 2b2183fb6d..9583ccfbdb 100644 --- a/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/utils/SystemBridgeError.kt +++ b/sysbridge/src/main/java/io/github/sds100/keymapper/sysbridge/utils/SystemBridgeError.kt @@ -4,4 +4,5 @@ import io.github.sds100.keymapper.common.utils.KMError sealed class SystemBridgeError : KMError() { data object Disconnected : SystemBridgeError() + data object WriteEvdevEventFailed : SystemBridgeError() } diff --git a/system/src/main/java/io/github/sds100/keymapper/system/devices/AndroidDevicesAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/devices/AndroidDevicesAdapter.kt index 923dc8972d..0c65c345c4 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/devices/AndroidDevicesAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/devices/AndroidDevicesAdapter.kt @@ -4,8 +4,6 @@ import android.bluetooth.BluetoothDevice import android.bluetooth.BluetoothManager import android.content.Context import android.hardware.input.InputManager -import android.os.Handler -import android.os.Looper import android.view.InputDevice import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext @@ -37,7 +35,8 @@ class AndroidDevicesAdapter @Inject constructor( private val bluetoothAdapter: io.github.sds100.keymapper.system.bluetooth.BluetoothAdapter, private val permissionAdapter: PermissionAdapter, private val coroutineScope: CoroutineScope, -) : DevicesAdapter { +) : DevicesAdapter, + InputManager.InputDeviceListener { private val ctx = context.applicationContext private val inputManager = ctx.getSystemService() @@ -69,40 +68,7 @@ class AndroidDevicesAdapter @Inject constructor( } } - inputManager?.apply { - registerInputDeviceListener( - object : InputManager.InputDeviceListener { - override fun onInputDeviceAdded(deviceId: Int) { - coroutineScope.launch { - val device = InputDevice.getDevice(deviceId) ?: return@launch - onInputDeviceConnect.emit( - InputDeviceUtils.createInputDeviceInfo(device), - ) - - updateInputDevices() - } - } - - override fun onInputDeviceRemoved(deviceId: Int) { - coroutineScope.launch { - connectedInputDevices.value.ifIsData { connectedInputDevices -> - val device = connectedInputDevices.find { it.id == deviceId } - ?: return@ifIsData - - onInputDeviceDisconnect.emit(device) - } - - updateInputDevices() - } - } - - override fun onInputDeviceChanged(deviceId: Int) { - updateInputDevices() - } - }, - Handler(Looper.getMainLooper()), - ) - } + inputManager?.registerInputDeviceListener(this, null) bluetoothAdapter.onDeviceConnect.onEach { device -> val currentValue = connectedBluetoothDevices.value @@ -146,6 +112,34 @@ class AndroidDevicesAdapter @Inject constructor( return InputDevice.getDevice(deviceId)?.let { InputDeviceUtils.createInputDeviceInfo(it) } } + override fun onInputDeviceAdded(deviceId: Int) { + coroutineScope.launch { + val device = InputDevice.getDevice(deviceId) ?: return@launch + onInputDeviceConnect.emit( + InputDeviceUtils.createInputDeviceInfo(device), + ) + + updateInputDevices() + } + } + + override fun onInputDeviceRemoved(deviceId: Int) { + coroutineScope.launch { + connectedInputDevices.value.ifIsData { connectedInputDevices -> + val device = connectedInputDevices.find { it.id == deviceId } + ?: return@ifIsData + + onInputDeviceDisconnect.emit(device) + } + + updateInputDevices() + } + } + + override fun onInputDeviceChanged(deviceId: Int) { + updateInputDevices() + } + private fun updateInputDevices() { val devices = mutableListOf() diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt index d234b680aa..560e60e42d 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/AndroidDisplayAdapter.kt @@ -7,6 +7,7 @@ import android.content.IntentFilter import android.hardware.display.DisplayManager import android.provider.Settings import android.view.Display +import android.view.OrientationEventListener import android.view.Surface import androidx.core.content.ContextCompat import androidx.core.content.getSystemService @@ -14,6 +15,7 @@ import dagger.hilt.android.qualifiers.ApplicationContext 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.PhysicalOrientation import io.github.sds100.keymapper.common.utils.SettingsUtils import io.github.sds100.keymapper.common.utils.SizeKM import io.github.sds100.keymapper.common.utils.Success @@ -28,6 +30,14 @@ import kotlinx.coroutines.flow.update import kotlinx.coroutines.flow.updateAndGet import kotlinx.coroutines.launch +/** + * Android implementation of DisplayAdapter. + * + * This is a Singleton that lives for the lifetime of the application. + * Listeners (DisplayManager.DisplayListener, BroadcastReceiver, OrientationEventListener) + * are intentionally not unregistered because the adapter needs to continuously track + * display state changes throughout the app's lifecycle. + */ @Singleton class AndroidDisplayAdapter @Inject constructor( @ApplicationContext private val context: Context, @@ -39,6 +49,12 @@ class AndroidDisplayAdapter @Inject constructor( * How much to change the brightness by. */ private const val BRIGHTNESS_CHANGE_STEP = 20 + + /** + * Tolerance in degrees for orientation detection. + * This helps avoid rapid switching at orientation boundaries. + */ + private const val ORIENTATION_TOLERANCE = 45 } private val ctx = context.applicationContext @@ -77,12 +93,28 @@ class AndroidDisplayAdapter @Inject constructor( override val cachedOrientation: Orientation get() = _orientation.value + private val _physicalOrientation = MutableStateFlow(PhysicalOrientation.PORTRAIT) + override val physicalOrientation: Flow = _physicalOrientation + override val cachedPhysicalOrientation: PhysicalOrientation + get() = _physicalOrientation.value + override val size: SizeKM get() = ctx.getRealDisplaySize() override val isAmbientDisplayEnabled: MutableStateFlow = MutableStateFlow(isAodEnabled()) + private val orientationEventListener = object : OrientationEventListener(ctx) { + override fun onOrientationChanged(orientationDegrees: Int) { + if (orientationDegrees == ORIENTATION_UNKNOWN) { + return + } + + val newPhysicalOrientation = degreesToPhysicalOrientation(orientationDegrees) + _physicalOrientation.update { newPhysicalOrientation } + } + } + init { displayManager.registerDisplayListener( object : DisplayManager.DisplayListener { @@ -115,6 +147,11 @@ class AndroidDisplayAdapter @Inject constructor( filter, ContextCompat.RECEIVER_NOT_EXPORTED, ) + + // Enable physical orientation detection + if (orientationEventListener.canDetectOrientation()) { + orientationEventListener.enable() + } } override fun isAutoRotateEnabled(): Boolean = @@ -253,4 +290,36 @@ class AndroidDisplayAdapter @Inject constructor( private fun isAodEnabled(): Boolean { return SettingsUtils.getSecureSetting(ctx, "doze_always_on") == 1 } + + /** + * Converts sensor orientation degrees to PhysicalOrientation. + * + * The orientation degrees from OrientationEventListener represent how much + * the device is rotated from its natural orientation: + * - 0°: Device is upright (portrait for most phones) + * - 90°: Device is rotated 90° counter-clockwise (landscape, home button on right) + * - 180°: Device is upside down (portrait inverted) + * - 270°: Device is rotated 90° clockwise (landscape, home button on left) + * + * Using a tolerance helps avoid rapid orientation changes at boundaries. + */ + private fun degreesToPhysicalOrientation(degrees: Int): PhysicalOrientation { + // OrientationEventListener returns 0-359 degrees. + // Handle wraparound at 0/360 boundary for portrait detection. + // Degrees outside the defined tolerance zones (e.g., 45-89° between portrait and landscape) + // are intentionally kept as the current orientation to provide hysteresis and prevent + // rapid orientation switching when the device is tilted at boundary angles. + return when { + degrees >= (360 - ORIENTATION_TOLERANCE) || + degrees < ORIENTATION_TOLERANCE -> + PhysicalOrientation.PORTRAIT + degrees in (90 - ORIENTATION_TOLERANCE) until (90 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.LANDSCAPE + degrees in (180 - ORIENTATION_TOLERANCE) until (180 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.PORTRAIT_INVERTED + degrees in (270 - ORIENTATION_TOLERANCE) until (270 + ORIENTATION_TOLERANCE) -> + PhysicalOrientation.LANDSCAPE_INVERTED + else -> _physicalOrientation.value // Keep current orientation in transition zone + } + } } diff --git a/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt index f8792d6fd4..377e148044 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/display/DisplayAdapter.kt @@ -2,6 +2,7 @@ package io.github.sds100.keymapper.system.display import io.github.sds100.keymapper.common.utils.KMResult import io.github.sds100.keymapper.common.utils.Orientation +import io.github.sds100.keymapper.common.utils.PhysicalOrientation import io.github.sds100.keymapper.common.utils.SizeKM import kotlinx.coroutines.flow.Flow @@ -9,6 +10,8 @@ interface DisplayAdapter { val isScreenOn: Flow val orientation: Flow val cachedOrientation: Orientation + val physicalOrientation: Flow + val cachedPhysicalOrientation: PhysicalOrientation val size: SizeKM val isAmbientDisplayEnabled: Flow diff --git a/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt b/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt index fa88349920..86fc6e4be6 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/inputevents/KMEvdevEvent.kt @@ -1,9 +1,10 @@ package io.github.sds100.keymapper.system.inputevents -import io.github.sds100.keymapper.common.models.EvdevDeviceHandle +import io.github.sds100.keymapper.common.models.EvdevDeviceInfo data class KMEvdevEvent( - val device: EvdevDeviceHandle, + val deviceId: Int, + val deviceInfo: EvdevDeviceInfo, val type: Int, val code: Int, val value: Int, diff --git a/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt index 6f33664c9e..bef0c78cef 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/permissions/AndroidPermissionAdapter.kt @@ -5,6 +5,7 @@ import android.app.NotificationManager import android.app.admin.DevicePolicyManager import android.content.ComponentName import android.content.Context +import android.content.pm.IPackageManager import android.content.pm.PackageManager.PERMISSION_GRANTED import android.os.Build import android.os.PowerManager @@ -20,6 +21,7 @@ import io.github.sds100.keymapper.common.BuildConfigProvider import io.github.sds100.keymapper.common.utils.Constants import io.github.sds100.keymapper.common.utils.KMError import io.github.sds100.keymapper.common.utils.KMResult +import io.github.sds100.keymapper.common.utils.firstBlocking import io.github.sds100.keymapper.common.utils.getIdentifier import io.github.sds100.keymapper.common.utils.onFailure import io.github.sds100.keymapper.common.utils.onSuccess @@ -71,16 +73,24 @@ class AndroidPermissionAdapter @Inject constructor( const val REQUEST_CODE_SHIZUKU_PERMISSION = 1 } - private val shizukuPermissionManager: IPermissionManager by lazy { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - HiddenApiBypass.addHiddenApiExemptions( - "Landroid/permission", - ) + private val shizukuPermissionManager: IPermissionManager? by lazy { + // Use IPackageManager instead on older versions + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.R) { + return@lazy null } + + HiddenApiBypass.addHiddenApiExemptions( + "Landroid/permission", + ) val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("permissionmgr")) IPermissionManager.Stub.asInterface(binder) } + private val shizukuPackageManager: IPackageManager? by lazy { + val binder = ShizukuBinderWrapper(SystemServiceHelper.getSystemService("package")) + IPackageManager.Stub.asInterface(binder) + } + private val ctx = context.applicationContext private val powerManager: PowerManager? = ctx.getSystemService() @@ -167,13 +177,22 @@ class AndroidPermissionAdapter @Inject constructor( } else if (shizukuAdapter.isStarted.value && isGranted(Permission.SHIZUKU)) { val userId = Process.myUserHandle()!!.getIdentifier() - PermissionManagerApis.grantPermission( - shizukuPermissionManager, - buildConfigProvider.packageName, - permissionName, - deviceId, - userId, - ) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + PermissionManagerApis.grantPermission( + shizukuPermissionManager!!, + buildConfigProvider.packageName, + permissionName, + deviceId, + userId, + ) + } else { + PermissionManagerApis.grantPermission( + shizukuPackageManager!!, + buildConfigProvider.packageName, + permissionName, + userId, + ) + } if (ContextCompat.checkSelfPermission(ctx, permissionName) == PERMISSION_GRANTED) { result = success() @@ -259,7 +278,7 @@ class AndroidPermissionAdapter @Inject constructor( Manifest.permission.SEND_SMS, ) == PERMISSION_GRANTED - Permission.ROOT -> suAdapter.isRootGranted.value + Permission.ROOT -> suAdapter.isRootGranted.firstBlocking() ?: false Permission.IGNORE_BATTERY_OPTIMISATION -> powerManager?.isIgnoringBatteryOptimizations(buildConfigProvider.packageName) ?: false diff --git a/system/src/main/java/io/github/sds100/keymapper/system/phone/AndroidPhoneAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/phone/AndroidPhoneAdapter.kt index eadc547009..6dc2dd58fe 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/phone/AndroidPhoneAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/phone/AndroidPhoneAdapter.kt @@ -185,13 +185,13 @@ class AndroidPhoneAdapter @Inject constructor( ) try { - if (SystemClock.uptimeMillis() - lastSmsTime < SMS_MIN_RATE_MILLIS) { + if (SystemClock.elapsedRealtime() - lastSmsTime < SMS_MIN_RATE_MILLIS) { Timber.d("SMS rate limit exceeded to protect against significant costs") return KMError.KeyMapperSmsRateLimit } smsManager.sendTextMessage(number, null, message, sentPendingIntent, null) - lastSmsTime = SystemClock.uptimeMillis() + lastSmsTime = SystemClock.elapsedRealtime() } catch (e: IllegalArgumentException) { return KMError.Exception(e) } diff --git a/system/src/main/java/io/github/sds100/keymapper/system/power/AndroidPowerAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/power/AndroidPowerAdapter.kt index bd9f0174ee..e392a55949 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/power/AndroidPowerAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/power/AndroidPowerAdapter.kt @@ -5,7 +5,6 @@ import android.content.Context import android.content.Intent import android.content.IntentFilter import android.os.BatteryManager -import android.os.Build import androidx.core.content.ContextCompat import androidx.core.content.getSystemService import dagger.hilt.android.qualifiers.ApplicationContext @@ -53,10 +52,5 @@ class AndroidPowerAdapter @Inject constructor(@ApplicationContext private val co ) } - private fun getIsCharging(): Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - batteryManager.isCharging - } else { - // no other way to synchronously get the information - false - } + private fun getIsCharging(): Boolean = batteryManager.isCharging } diff --git a/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt b/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt index d0ce05af92..5ad4444b46 100644 --- a/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt +++ b/system/src/main/java/io/github/sds100/keymapper/system/root/SuAdapter.kt @@ -5,11 +5,12 @@ import io.github.sds100.keymapper.system.shell.BaseShellAdapter import io.github.sds100.keymapper.system.shell.ShellAdapter import javax.inject.Inject import javax.inject.Singleton +import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job +import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.update import kotlinx.coroutines.launch import kotlinx.coroutines.withContext @@ -19,7 +20,7 @@ import timber.log.Timber class SuAdapterImpl @Inject constructor(private val coroutineScope: CoroutineScope) : BaseShellAdapter(), SuAdapter { - override val isRootGranted: MutableStateFlow = MutableStateFlow(false) + override val isRootGranted: MutableStateFlow = MutableStateFlow(null) private var invalidateJob: Job? = null @@ -47,14 +48,10 @@ class SuAdapterImpl @Inject constructor(private val coroutineScope: CoroutineSco // Close the shell so a new one is started without root permission. val isRooted = getIsRooted() isRootGranted.update { isRooted } - - if (isRooted) { - Timber.i("Root access granted") - } else { - Timber.i("Root access denied") - } } catch (e: Exception) { - Timber.e("Exception invalidating root detection: $e") + if (e !is CancellationException) { + Timber.e("Exception invalidating root detection: $e") + } } } @@ -68,7 +65,7 @@ class SuAdapterImpl @Inject constructor(private val coroutineScope: CoroutineSco } interface SuAdapter : ShellAdapter { - val isRootGranted: StateFlow + val isRootGranted: Flow fun requestPermission() } diff --git a/systemstubs/src/main/java/android/app/ActivityTaskManagerApis.kt b/systemstubs/src/main/java/android/app/ActivityTaskManagerApis.kt index ac3a80e5f6..dc7705c1f7 100644 --- a/systemstubs/src/main/java/android/app/ActivityTaskManagerApis.kt +++ b/systemstubs/src/main/java/android/app/ActivityTaskManagerApis.kt @@ -18,6 +18,22 @@ object ActivityTaskManagerApis { keepIntentExtra, displayId, ) + } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + try { + return activityTaskManager.getTasks( + maxNum, + filterOnlyVisibleRecents, + keepIntentExtra, + ) + } catch (_: NoSuchMethodError) { + // In later revisions of Android 13 this method was added. + return activityTaskManager.getTasks( + maxNum, + filterOnlyVisibleRecents, + keepIntentExtra, + displayId, + ) + } } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { return activityTaskManager.getTasks(maxNum, filterOnlyVisibleRecents, keepIntentExtra) } else { diff --git a/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt b/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt index 8ef810c186..247bbd02d6 100644 --- a/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt +++ b/systemstubs/src/main/java/android/permission/PermissionManagerApis.kt @@ -1,8 +1,11 @@ package android.permission +import android.content.pm.IPackageManager import android.os.Build +import androidx.annotation.RequiresApi object PermissionManagerApis { + @RequiresApi(Build.VERSION_CODES.R) fun grantPermission( permissionManager: IPermissionManager, packageName: String, @@ -37,12 +40,6 @@ object PermissionManagerApis { } } // In Android 11 this method was moved from IPackageManager to IPermissionManager. - } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { - permissionManager.grantRuntimePermission( - packageName, - permission, - userId, - ) } else { permissionManager.grantRuntimePermission( packageName, @@ -51,4 +48,14 @@ object PermissionManagerApis { ) } } + + // Used on Android 10 and older. + fun grantPermission( + packageManager: IPackageManager, + packageName: String, + permission: String, + userId: Int, + ) { + packageManager.grantRuntimePermission(packageName, permission, userId) + } }