Clean up compatibility support code
This commit is contained in:
@@ -4,43 +4,68 @@ NavButtons is an Android LSPosed module for customizing the three-button navigat
|
||||
|
||||
It lets you choose what should happen when Back, Home, or Recents is pressed or long-pressed, and lets you configure the long-press duration per button.
|
||||
|
||||
It also adds side arrow keys into the navigation bar for cursor movement.
|
||||
|
||||
## AI Assistance
|
||||
|
||||
This project was developed with help from AI-assisted coding tools.
|
||||
|
||||
## Features
|
||||
|
||||
- Configure press and long-press actions for Back, Home, and Recents.
|
||||
- Keep the stock Android behavior with the `Stock` action.
|
||||
- Disable a press with the `None` action.
|
||||
- Reassign navigation actions: Back, Home, Assistant, and Recents.
|
||||
- Kill the foreground app.
|
||||
- Toggle auto-rotate.
|
||||
- Toggle the flashlight.
|
||||
- Individually configure custom long-press durations for all buttons.
|
||||
- Add optional side-arrow buttons for moving the text cursor left and right.
|
||||
- Configure side-arrow long-press behavior, including repeated cursor movement.
|
||||
- Long-press side arrows for repeated cursor movement with configurable speed.
|
||||
- Test long-press durations from the settings screen.
|
||||
|
||||
Supported actions:
|
||||
|
||||
- `Stock`: keep the default behavior for that button.
|
||||
- `None`: suppress the button.
|
||||
- `Back`: perform the normal Back action.
|
||||
- `Home`: perform the normal Home action.
|
||||
- `Assistant`: open the assistant.
|
||||
- `Recents`: open recent apps.
|
||||
- `Kill foreground app`: force close the currently active app.
|
||||
- `Toggle auto-rotate`: toggle the device's auto-rotate setting.
|
||||
- `Toggle flashlight`: toggle the flashlight.
|
||||
|
||||
## Requirements
|
||||
|
||||
- Android device using three-button navigation.
|
||||
- LSPosed with Xposed API 101 or newer.
|
||||
- Android 14 / API 34 or newer.
|
||||
- The module enabled for the relevant navigation packages.
|
||||
- Android (One UI, AOSP, Google APIs, LineageOS) version listed in the compatibility list.
|
||||
- The module scope enabled for the relevant navigation packages.
|
||||
|
||||
The module declares scope entries for:
|
||||
|
||||
- `android` and `system`, used for privileged actions such as killing the foreground app.
|
||||
- `com.android.systemui`, used for phone navigation bars.
|
||||
- `com.android.launcher3` and `com.google.android.apps.nexuslauncher`, used for AOSP/Google taskbar navigation.
|
||||
- `com.sec.android.app.launcher` and `com.samsung.systemui.navillera`, used for Samsung/One UI navigation.
|
||||
- `com.sec.android.app.launcher`, used for Samsung/One UI folded navigation.
|
||||
|
||||
## Tested On
|
||||
## Compatibility
|
||||
|
||||
- Samsung Galaxy Z Fold5 running One UI 8.0 / Android 16 with Magisk and LSPosed.
|
||||
- AOSP emulator API 34 / Android 14 with Magisk and LSPosed.
|
||||
- AOSP emulator API 35 / Android 15 with Magisk and LSPosed.
|
||||
- AOSP emulator API 36 / Android 16 with Magisk and LSPosed.
|
||||
- Android 16 Google APIs emulator with Magisk and LSPosed.
|
||||
Supported targets are expected to support all features.
|
||||
|
||||
The emulator tests covered button press overrides, long-press overrides with custom duration, side-arrow visibility, side-arrow tinting, enabling/disabling side arrows, and side-arrow long-press repeat behavior where supported by the emulator navigation implementation. Actual device testing with others than the Galaxy Z Fold 5 has not been done. Compatibility may vary between Android versions and vendor System UI / launcher implementations.
|
||||
Samsung, tested on real hardware:
|
||||
|
||||
## AI Assistance
|
||||
- One UI 8 / Android 16 on a Galaxy Z Fold5.
|
||||
|
||||
This project was developed with help from AI-assisted coding tools.
|
||||
AOSP emulator images:
|
||||
|
||||
- Android 14 / API 34.
|
||||
- Android 15 / API 35.
|
||||
- Android 16 / API 36.
|
||||
|
||||
Google APIs emulator images:
|
||||
|
||||
- Android 15 / API 35.
|
||||
- Android 16 / API 36.
|
||||
|
||||
LineageOS emulator images:
|
||||
|
||||
- LineageOS 22.2 / Android 15 / API 35.
|
||||
- LineageOS 23.2 / Android 16 / API 36.
|
||||
|
||||
Emulator validation used Magisk and LSPosed. Emulator results confirm compatibility with those images, not with every ROM derived from the same Android version. Vendor System UI and launcher implementations can differ enough to require separate support. On emulators, flashlight support is validated by confirming that the flashlight signal is sent; real hardware behavior depends on the device camera/flash implementation.
|
||||
|
||||
@@ -86,9 +86,5 @@ dependencies {
|
||||
implementation("androidx.annotation:annotation:1.7.1")
|
||||
implementation("io.github.libxposed:service:101.0.0")
|
||||
implementation("org.lsposed.hiddenapibypass:hiddenapibypass:6.1")
|
||||
compileOnly("io.github.libxposed:api:101.0.0")
|
||||
|
||||
testImplementation("junit:junit:4.13.2")
|
||||
androidTestImplementation("androidx.test.ext:junit:1.1.5")
|
||||
androidTestImplementation("androidx.test.espresso:espresso-core:3.5.1")
|
||||
compileOnly("io.github.libxposed:api:102.0.0")
|
||||
}
|
||||
|
||||
Vendored
+2
-23
@@ -1,28 +1,7 @@
|
||||
# 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
|
||||
|
||||
-adaptresourcefilecontents META-INF/xposed/java_init.list
|
||||
-dontobfuscate
|
||||
|
||||
-keep class se.ajpanton.navbuttons.NavButtons
|
||||
-keepclassmembers class se.ajpanton.navbuttons.NavButtons {
|
||||
-keep,allowoptimization,allowobfuscation public class * extends io.github.libxposed.api.XposedModule {
|
||||
public <init>();
|
||||
}
|
||||
|
||||
@@ -6,13 +6,16 @@
|
||||
|
||||
<queries>
|
||||
<package android:name="org.lsposed.manager" />
|
||||
<package android:name="com.android.systemui" />
|
||||
<package android:name="com.android.launcher3" />
|
||||
<package android:name="com.google.android.apps.nexuslauncher" />
|
||||
<package android:name="com.sec.android.app.launcher" />
|
||||
</queries>
|
||||
|
||||
<application
|
||||
android:allowBackup="true"
|
||||
android:dataExtractionRules="@xml/data_extraction_rules"
|
||||
android:allowBackup="false"
|
||||
android:description="@string/xposed_desc"
|
||||
android:fullBackupContent="@xml/backup_rules"
|
||||
android:forceQueryable="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:roundIcon="@mipmap/ic_launcher_round"
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -81,6 +81,31 @@ class SettingsActivity : Activity() {
|
||||
applySectionTopMargin(sideArrowsSectionView)
|
||||
SideArrowsSectionController(sideArrowsSectionView).bind()
|
||||
sectionsContainer.addView(sideArrowsSectionView)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
|
||||
private fun sendSettingsChangedBroadcast() {
|
||||
val intent = Intent(NavButtonSettingsStore.ACTION_SETTINGS_CHANGED)
|
||||
NavButtonId.entries.forEach { buttonId ->
|
||||
val config = settingsStore.getConfig(buttonId)
|
||||
intent.putExtra(NavButtonSettingsStore.pressActionKey(buttonId), config.pressAction.storageValue)
|
||||
intent.putExtra(NavButtonSettingsStore.longPressActionKey(buttonId), config.longPressAction.storageValue)
|
||||
intent.putExtra(NavButtonSettingsStore.longPressDurationKey(buttonId), config.longPressDurationMs)
|
||||
}
|
||||
intent.putExtra(NavButtonSettingsStore.KEY_SIDE_ARROWS_ENABLED, settingsStore.isSideArrowsEnabled())
|
||||
intent.putExtra(
|
||||
NavButtonSettingsStore.KEY_SIDE_ARROW_LONG_PRESS_ACTION,
|
||||
settingsStore.getSideArrowLongPressAction().storageValue,
|
||||
)
|
||||
intent.putExtra(
|
||||
NavButtonSettingsStore.KEY_SIDE_ARROW_LONG_PRESS_DURATION_MS,
|
||||
settingsStore.getSideArrowLongPressDurationMs(),
|
||||
)
|
||||
intent.putExtra(
|
||||
NavButtonSettingsStore.KEY_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||
settingsStore.getSideArrowRepeatStepsPerSecond(),
|
||||
)
|
||||
sendBroadcast(intent)
|
||||
}
|
||||
|
||||
private fun applySectionTopMargin(sectionView: View) {
|
||||
@@ -246,7 +271,10 @@ class SettingsActivity : Activity() {
|
||||
NavButtonAction.entries,
|
||||
selectedAction,
|
||||
NavButtonAction::labelRes,
|
||||
) { action -> storeAction(buttonId, action) }
|
||||
) { action ->
|
||||
storeAction(buttonId, action)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindDurationInput() {
|
||||
@@ -280,6 +308,7 @@ class SettingsActivity : Activity() {
|
||||
if (parsed in NavButtonSettingsStore.MIN_DURATION_MS..NavButtonSettingsStore.MAX_DURATION_MS) {
|
||||
lastValidDurationMs = parsed
|
||||
settingsStore.setLongPressDurationMs(buttonId, parsed)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -327,6 +356,7 @@ class SettingsActivity : Activity() {
|
||||
lastValidDurationMs = sanitized
|
||||
if (persist) {
|
||||
settingsStore.setLongPressDurationMs(buttonId, sanitized)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -374,12 +404,7 @@ class SettingsActivity : Activity() {
|
||||
sideArrowsCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||
settingsStore.setSideArrowsEnabled(isChecked)
|
||||
updateOptionsEnabled()
|
||||
sendBroadcast(
|
||||
Intent(NavButtonSettingsStore.ACTION_SETTINGS_CHANGED).putExtra(
|
||||
NavButtonSettingsStore.EXTRA_SIDE_ARROWS_ENABLED,
|
||||
isChecked,
|
||||
),
|
||||
)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -390,8 +415,9 @@ class SettingsActivity : Activity() {
|
||||
settingsStore.getSideArrowLongPressAction(),
|
||||
SideArrowLongPressAction::labelRes,
|
||||
) { action ->
|
||||
settingsStore.setSideArrowLongPressAction(action)
|
||||
updateRepeatSpeedVisibility(action)
|
||||
settingsStore.setSideArrowLongPressAction(action)
|
||||
updateRepeatSpeedVisibility(action)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
updateRepeatSpeedVisibility(settingsStore.getSideArrowLongPressAction())
|
||||
}
|
||||
@@ -429,6 +455,7 @@ class SettingsActivity : Activity() {
|
||||
) {
|
||||
lastValidRepeatSpeed = parsed
|
||||
settingsStore.setSideArrowRepeatStepsPerSecond(parsed)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -474,6 +501,7 @@ class SettingsActivity : Activity() {
|
||||
if (parsed in NavButtonSettingsStore.MIN_DURATION_MS..NavButtonSettingsStore.MAX_DURATION_MS) {
|
||||
lastValidDurationMs = parsed
|
||||
settingsStore.setSideArrowLongPressDurationMs(parsed)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
})
|
||||
@@ -536,6 +564,7 @@ class SettingsActivity : Activity() {
|
||||
lastValidRepeatSpeed = sanitized
|
||||
if (persist) {
|
||||
settingsStore.setSideArrowRepeatStepsPerSecond(sanitized)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -590,6 +619,7 @@ class SettingsActivity : Activity() {
|
||||
lastValidDurationMs = sanitized
|
||||
if (persist) {
|
||||
settingsStore.setSideArrowLongPressDurationMs(sanitized)
|
||||
sendSettingsChangedBroadcast()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
<resources>
|
||||
<style name="Theme.NavButtons" parent="@android:style/Theme.DeviceDefault.DayNight">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowBackground">@drawable/bg_settings_window</item>
|
||||
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
@@ -3,6 +3,5 @@
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowBackground">@drawable/bg_settings_window</item>
|
||||
<item name="android:windowOptOutEdgeToEdgeEnforcement">true</item>
|
||||
</style>
|
||||
</resources>
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample backup rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/guide/topics/data/autobackup
|
||||
for details.
|
||||
Note: This file is ignored for devices older that API 31
|
||||
See https://developer.android.com/about/versions/12/backup-restore
|
||||
-->
|
||||
<full-backup-content>
|
||||
<!--
|
||||
<include domain="sharedpref" path="."/>
|
||||
<exclude domain="sharedpref" path="device.xml"/>
|
||||
-->
|
||||
</full-backup-content>
|
||||
@@ -1,19 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
Sample data extraction rules file; uncomment and customize as necessary.
|
||||
See https://developer.android.com/about/versions/12/backup-restore#xml-changes
|
||||
for details.
|
||||
-->
|
||||
<data-extraction-rules>
|
||||
<cloud-backup>
|
||||
<!-- TODO: Use <include> and <exclude> to control what is backed up.
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
-->
|
||||
</cloud-backup>
|
||||
<!--
|
||||
<device-transfer>
|
||||
<include .../>
|
||||
<exclude .../>
|
||||
</device-transfer>
|
||||
-->
|
||||
</data-extraction-rules>
|
||||
@@ -1,3 +1,3 @@
|
||||
minApiVersion=101
|
||||
targetApiVersion=101
|
||||
targetApiVersion=102
|
||||
staticScope=true
|
||||
|
||||
@@ -4,4 +4,3 @@ com.android.systemui
|
||||
com.android.launcher3
|
||||
com.google.android.apps.nexuslauncher
|
||||
com.sec.android.app.launcher
|
||||
com.samsung.systemui.navillera
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id("com.android.application") version "9.1.1" apply false
|
||||
}
|
||||
|
||||
@@ -1,21 +1,3 @@
|
||||
# Project-wide Gradle settings.
|
||||
# IDE (e.g. Android Studio) users:
|
||||
# Gradle settings configured through the IDE *will override*
|
||||
# any settings specified in this file.
|
||||
# For more details on how to configure your build environment visit
|
||||
# http://www.gradle.org/docs/current/userguide/build_environment.html
|
||||
# Specifies the JVM arguments used for the daemon process.
|
||||
# The setting is particularly useful for tweaking memory settings.
|
||||
org.gradle.jvmargs=-Xmx2048m -Dfile.encoding=UTF-8
|
||||
# When configured, Gradle will run in incubating parallel mode.
|
||||
# This option should only be used with decoupled projects. More details, visit
|
||||
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
|
||||
# org.gradle.parallel=true
|
||||
# AndroidX package structure to make it clearer which packages are bundled with the
|
||||
# Android operating system, and which are packaged with your app's APK
|
||||
# https://developer.android.com/topic/libraries/support-library/androidx-rn
|
||||
android.useAndroidX=true
|
||||
# Enables namespacing of each library's R class so that its R class includes only the
|
||||
# resources declared in the library itself and none from the library's dependencies,
|
||||
# thereby reducing the size of the R class for that library
|
||||
android.nonTransitiveRClass=true
|
||||
|
||||
Reference in New Issue
Block a user