Add side arrow navigation buttons
This commit is contained in:
@@ -4,3 +4,4 @@ build/
|
|||||||
app/build/
|
app/build/
|
||||||
local/
|
local/
|
||||||
local.properties
|
local.properties
|
||||||
|
*.log
|
||||||
|
|||||||
@@ -39,6 +39,22 @@ enum class NavButtonAction(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
enum class SideArrowLongPressAction(
|
||||||
|
val storageValue: String,
|
||||||
|
@get:StringRes val labelRes: Int,
|
||||||
|
) {
|
||||||
|
NOTHING("nothing", R.string.side_arrow_long_press_nothing),
|
||||||
|
SINGLE_STEP("single_step", R.string.side_arrow_long_press_single_step),
|
||||||
|
MULTIPLE_STEPS("multiple_steps", R.string.side_arrow_long_press_multiple_steps),
|
||||||
|
;
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
fun fromStorageValue(value: String?): SideArrowLongPressAction {
|
||||||
|
return entries.firstOrNull { it.storageValue == value } ?: MULTIPLE_STEPS
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
data class NavButtonConfig(
|
data class NavButtonConfig(
|
||||||
val pressAction: NavButtonAction,
|
val pressAction: NavButtonAction,
|
||||||
val longPressAction: NavButtonAction,
|
val longPressAction: NavButtonAction,
|
||||||
@@ -91,6 +107,61 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isSideArrowsEnabled(): Boolean {
|
||||||
|
return currentPreferences().getBoolean(KEY_SIDE_ARROWS_ENABLED, false)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSideArrowsEnabled(enabled: Boolean) {
|
||||||
|
persistChanges {
|
||||||
|
putBoolean(KEY_SIDE_ARROWS_ENABLED, enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSideArrowLongPressAction(): SideArrowLongPressAction {
|
||||||
|
return SideArrowLongPressAction.fromStorageValue(
|
||||||
|
currentPreferences().getString(KEY_SIDE_ARROW_LONG_PRESS_ACTION, null),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSideArrowLongPressAction(action: SideArrowLongPressAction) {
|
||||||
|
persistChanges {
|
||||||
|
putString(KEY_SIDE_ARROW_LONG_PRESS_ACTION, action.storageValue)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSideArrowRepeatStepsPerSecond(): Float {
|
||||||
|
return sanitizeSideArrowRepeatSpeed(
|
||||||
|
currentPreferences().getFloat(
|
||||||
|
KEY_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
DEFAULT_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSideArrowRepeatStepsPerSecond(stepsPerSecond: Float) {
|
||||||
|
persistChanges {
|
||||||
|
putFloat(
|
||||||
|
KEY_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
sanitizeSideArrowRepeatSpeed(stepsPerSecond),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getSideArrowLongPressDurationMs(): Int {
|
||||||
|
return sanitizeDuration(
|
||||||
|
currentPreferences().getInt(
|
||||||
|
KEY_SIDE_ARROW_LONG_PRESS_DURATION_MS,
|
||||||
|
defaultLongPressDurationMs(),
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun setSideArrowLongPressDurationMs(durationMs: Int) {
|
||||||
|
persistChanges {
|
||||||
|
putInt(KEY_SIDE_ARROW_LONG_PRESS_DURATION_MS, sanitizeDuration(durationMs))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun defaultLongPressDurationMs(): Int {
|
fun defaultLongPressDurationMs(): Int {
|
||||||
return sanitizeDuration(ViewConfiguration.getLongPressTimeout())
|
return sanitizeDuration(ViewConfiguration.getLongPressTimeout())
|
||||||
}
|
}
|
||||||
@@ -99,6 +170,17 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
return durationMs.coerceIn(MIN_LONG_PRESS_DURATION_MS, MAX_LONG_PRESS_DURATION_MS)
|
return durationMs.coerceIn(MIN_LONG_PRESS_DURATION_MS, MAX_LONG_PRESS_DURATION_MS)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun sanitizeSideArrowRepeatSpeed(stepsPerSecond: Float): Float {
|
||||||
|
return if (stepsPerSecond.isFinite()) {
|
||||||
|
stepsPerSecond.coerceIn(
|
||||||
|
MIN_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
MAX_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
DEFAULT_SIDE_ARROW_REPEAT_STEPS_PER_SECOND
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun currentPreferences(): SharedPreferences {
|
private fun currentPreferences(): SharedPreferences {
|
||||||
return remotePreferences ?: localPreferences
|
return remotePreferences ?: localPreferences
|
||||||
}
|
}
|
||||||
@@ -129,9 +211,15 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
companion object {
|
companion object {
|
||||||
private const val TAG = "NavButtons"
|
private const val TAG = "NavButtons"
|
||||||
const val PREFERENCES_NAME = "nav_button_settings"
|
const val PREFERENCES_NAME = "nav_button_settings"
|
||||||
|
const val ACTION_SETTINGS_CHANGED = "se.ajpanton.navbuttons.SETTINGS_CHANGED"
|
||||||
|
const val EXTRA_SIDE_ARROWS_ENABLED = "side_arrows_enabled"
|
||||||
const val KEY_PRESS_ACTION = "press_action"
|
const val KEY_PRESS_ACTION = "press_action"
|
||||||
const val KEY_LONG_PRESS_ACTION = "long_press_action"
|
const val KEY_LONG_PRESS_ACTION = "long_press_action"
|
||||||
const val KEY_LONG_PRESS_DURATION_MS = "long_press_duration_ms"
|
const val KEY_LONG_PRESS_DURATION_MS = "long_press_duration_ms"
|
||||||
|
const val KEY_SIDE_ARROWS_ENABLED = "side_arrows_enabled"
|
||||||
|
const val KEY_SIDE_ARROW_LONG_PRESS_ACTION = "side_arrow_long_press_action"
|
||||||
|
const val KEY_SIDE_ARROW_REPEAT_STEPS_PER_SECOND = "side_arrow_repeat_steps_per_second"
|
||||||
|
const val KEY_SIDE_ARROW_LONG_PRESS_DURATION_MS = "side_arrow_long_press_duration_ms"
|
||||||
private const val KEY_LAST_UPDATED_AT_MS = "last_updated_at_ms"
|
private const val KEY_LAST_UPDATED_AT_MS = "last_updated_at_ms"
|
||||||
|
|
||||||
const val MIN_LONG_PRESS_DURATION_MS = 100
|
const val MIN_LONG_PRESS_DURATION_MS = 100
|
||||||
@@ -141,6 +229,10 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
const val DURATION_STEP_MS = 100
|
const val DURATION_STEP_MS = 100
|
||||||
const val HOLD_REPEAT_START_DELAY_MS = 700L
|
const val HOLD_REPEAT_START_DELAY_MS = 700L
|
||||||
const val HOLD_REPEAT_INTERVAL_MS = 100L
|
const val HOLD_REPEAT_INTERVAL_MS = 100L
|
||||||
|
const val DEFAULT_SIDE_ARROW_REPEAT_STEPS_PER_SECOND = 20f
|
||||||
|
const val MIN_SIDE_ARROW_REPEAT_STEPS_PER_SECOND = 0.1f
|
||||||
|
const val MAX_SIDE_ARROW_REPEAT_STEPS_PER_SECOND = 100f
|
||||||
|
const val SIDE_ARROW_REPEAT_SPEED_STEP = 1f
|
||||||
|
|
||||||
fun pressActionKey(buttonId: NavButtonId): String = key(buttonId, KEY_PRESS_ACTION)
|
fun pressActionKey(buttonId: NavButtonId): String = key(buttonId, KEY_PRESS_ACTION)
|
||||||
|
|
||||||
@@ -161,13 +253,15 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
pressActionKey(buttonId),
|
pressActionKey(buttonId),
|
||||||
longPressActionKey(buttonId),
|
longPressActionKey(buttonId),
|
||||||
)
|
)
|
||||||
}
|
} + KEY_SIDE_ARROW_LONG_PRESS_ACTION
|
||||||
}
|
}
|
||||||
|
|
||||||
private val durationKeys by lazy(LazyThreadSafetyMode.NONE) {
|
private val durationKeys by lazy(LazyThreadSafetyMode.NONE) {
|
||||||
NavButtonId.entries.map(::longPressDurationKey)
|
NavButtonId.entries.map(::longPressDurationKey) + KEY_SIDE_ARROW_LONG_PRESS_DURATION_MS
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private val floatKeys = listOf(KEY_SIDE_ARROW_REPEAT_STEPS_PER_SECOND)
|
||||||
|
|
||||||
private fun key(buttonId: NavButtonId, suffix: String): String {
|
private fun key(buttonId: NavButtonId, suffix: String): String {
|
||||||
return "${buttonId.preferencePrefix}_$suffix"
|
return "${buttonId.preferencePrefix}_$suffix"
|
||||||
}
|
}
|
||||||
@@ -220,7 +314,10 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun hasStoredConfig(preferences: SharedPreferences): Boolean {
|
private fun hasStoredConfig(preferences: SharedPreferences): Boolean {
|
||||||
return actionKeys.any(preferences::contains) || durationKeys.any(preferences::contains)
|
return actionKeys.any(preferences::contains) ||
|
||||||
|
durationKeys.any(preferences::contains) ||
|
||||||
|
floatKeys.any(preferences::contains) ||
|
||||||
|
preferences.contains(KEY_SIDE_ARROWS_ENABLED)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyPreferences(source: SharedPreferences, target: SharedPreferences) {
|
private fun copyPreferences(source: SharedPreferences, target: SharedPreferences) {
|
||||||
@@ -242,6 +339,27 @@ class NavButtonSettingsStore(context: Context) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (key in floatKeys) {
|
||||||
|
if (source.contains(key)) {
|
||||||
|
editor.putFloat(
|
||||||
|
key,
|
||||||
|
source.getFloat(key, DEFAULT_SIDE_ARROW_REPEAT_STEPS_PER_SECOND)
|
||||||
|
.coerceIn(
|
||||||
|
MIN_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
MAX_SIDE_ARROW_REPEAT_STEPS_PER_SECOND,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
editor.remove(key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (source.contains(KEY_SIDE_ARROWS_ENABLED)) {
|
||||||
|
editor.putBoolean(KEY_SIDE_ARROWS_ENABLED, source.getBoolean(KEY_SIDE_ARROWS_ENABLED, false))
|
||||||
|
} else {
|
||||||
|
editor.remove(KEY_SIDE_ARROWS_ENABLED)
|
||||||
|
}
|
||||||
|
|
||||||
if (source.contains(KEY_LAST_UPDATED_AT_MS)) {
|
if (source.contains(KEY_LAST_UPDATED_AT_MS)) {
|
||||||
editor.putLong(KEY_LAST_UPDATED_AT_MS, source.getLong(KEY_LAST_UPDATED_AT_MS, 0L))
|
editor.putLong(KEY_LAST_UPDATED_AT_MS, source.getLong(KEY_LAST_UPDATED_AT_MS, 0L))
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,7 @@
|
|||||||
package se.ajpanton.navbuttons
|
package se.ajpanton.navbuttons
|
||||||
|
|
||||||
import android.app.Activity
|
import android.app.Activity
|
||||||
|
import android.content.Intent
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
import android.os.Handler
|
import android.os.Handler
|
||||||
import android.os.Looper
|
import android.os.Looper
|
||||||
@@ -13,10 +14,15 @@ import android.view.ViewGroup
|
|||||||
import android.widget.AdapterView
|
import android.widget.AdapterView
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.Button
|
import android.widget.Button
|
||||||
|
import android.widget.CheckBox
|
||||||
import android.widget.EditText
|
import android.widget.EditText
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.Spinner
|
import android.widget.Spinner
|
||||||
import android.widget.TextView
|
import android.widget.TextView
|
||||||
|
import android.widget.Toast
|
||||||
|
import java.text.DecimalFormatSymbols
|
||||||
|
import java.util.Locale
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
|
||||||
class SettingsActivity : Activity() {
|
class SettingsActivity : Activity() {
|
||||||
private val handler = Handler(Looper.getMainLooper())
|
private val handler = Handler(Looper.getMainLooper())
|
||||||
@@ -61,6 +67,24 @@ class SettingsActivity : Activity() {
|
|||||||
false,
|
false,
|
||||||
)
|
)
|
||||||
if (index > 0) {
|
if (index > 0) {
|
||||||
|
applySectionTopMargin(sectionView)
|
||||||
|
}
|
||||||
|
|
||||||
|
NavButtonSectionController(sectionView, buttonId).bind()
|
||||||
|
sectionsContainer.addView(sectionView)
|
||||||
|
}
|
||||||
|
|
||||||
|
val sideArrowsSectionView = inflater.inflate(
|
||||||
|
R.layout.item_side_arrows_section,
|
||||||
|
sectionsContainer,
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
applySectionTopMargin(sideArrowsSectionView)
|
||||||
|
SideArrowsSectionController(sideArrowsSectionView).bind()
|
||||||
|
sectionsContainer.addView(sideArrowsSectionView)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applySectionTopMargin(sectionView: View) {
|
||||||
val marginTop = resources.getDimensionPixelSize(R.dimen.section_spacing)
|
val marginTop = resources.getDimensionPixelSize(R.dimen.section_spacing)
|
||||||
val layoutParams = sectionView.layoutParams as? ViewGroup.MarginLayoutParams
|
val layoutParams = sectionView.layoutParams as? ViewGroup.MarginLayoutParams
|
||||||
if (layoutParams != null) {
|
if (layoutParams != null) {
|
||||||
@@ -75,11 +99,6 @@ class SettingsActivity : Activity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
NavButtonSectionController(sectionView, buttonId).bind()
|
|
||||||
sectionsContainer.addView(sectionView)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun startTestPreview(durationMs: Int) {
|
private fun startTestPreview(durationMs: Int) {
|
||||||
stopTestPreview()
|
stopTestPreview()
|
||||||
previewScheduled = true
|
previewScheduled = true
|
||||||
@@ -96,6 +115,45 @@ class SettingsActivity : Activity() {
|
|||||||
previewOverlay.visibility = View.GONE
|
previewOverlay.visibility = View.GONE
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun attachRepeatButton(button: Button, action: () -> Unit) {
|
||||||
|
var repeated = false
|
||||||
|
val repeatRunnable = object : Runnable {
|
||||||
|
override fun run() {
|
||||||
|
repeated = true
|
||||||
|
action()
|
||||||
|
handler.postDelayed(this, NavButtonSettingsStore.HOLD_REPEAT_INTERVAL_MS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setOnClickListener {
|
||||||
|
if (!repeated) {
|
||||||
|
action()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button.setOnTouchListener { _, event ->
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
repeated = false
|
||||||
|
handler.postDelayed(
|
||||||
|
repeatRunnable,
|
||||||
|
NavButtonSettingsStore.HOLD_REPEAT_START_DELAY_MS,
|
||||||
|
)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP,
|
||||||
|
MotionEvent.ACTION_CANCEL,
|
||||||
|
-> {
|
||||||
|
handler.removeCallbacks(repeatRunnable)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private inner class NavButtonSectionController(
|
private inner class NavButtonSectionController(
|
||||||
sectionView: View,
|
sectionView: View,
|
||||||
private val buttonId: NavButtonId,
|
private val buttonId: NavButtonId,
|
||||||
@@ -228,45 +286,6 @@ class SettingsActivity : Activity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun attachRepeatButton(button: Button, action: () -> Unit) {
|
|
||||||
var repeated = false
|
|
||||||
val repeatRunnable = object : Runnable {
|
|
||||||
override fun run() {
|
|
||||||
repeated = true
|
|
||||||
action()
|
|
||||||
handler.postDelayed(this, NavButtonSettingsStore.HOLD_REPEAT_INTERVAL_MS)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.setOnClickListener {
|
|
||||||
if (!repeated) {
|
|
||||||
action()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button.setOnTouchListener { view, event ->
|
|
||||||
when (event.actionMasked) {
|
|
||||||
MotionEvent.ACTION_DOWN -> {
|
|
||||||
repeated = false
|
|
||||||
handler.postDelayed(
|
|
||||||
repeatRunnable,
|
|
||||||
NavButtonSettingsStore.HOLD_REPEAT_START_DELAY_MS,
|
|
||||||
)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
MotionEvent.ACTION_UP,
|
|
||||||
MotionEvent.ACTION_CANCEL,
|
|
||||||
-> {
|
|
||||||
handler.removeCallbacks(repeatRunnable)
|
|
||||||
false
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun adjustDuration(deltaMs: Int) {
|
private fun adjustDuration(deltaMs: Int) {
|
||||||
val current = resolveDurationForUse()
|
val current = resolveDurationForUse()
|
||||||
val updated = (current + deltaMs).coerceIn(
|
val updated = (current + deltaMs).coerceIn(
|
||||||
@@ -303,4 +322,346 @@ class SettingsActivity : Activity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private inner class SideArrowsSectionController(sectionView: View) {
|
||||||
|
private val sideArrowsCheckbox: CheckBox = sectionView.findViewById(R.id.side_arrows_checkbox)
|
||||||
|
private val optionsContainer: LinearLayout =
|
||||||
|
sectionView.findViewById(R.id.side_arrows_options_container)
|
||||||
|
private val longPressActionSpinner: Spinner =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_long_press_action_spinner)
|
||||||
|
private val repeatSpeedContainer: LinearLayout =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_repeat_speed_container)
|
||||||
|
private val repeatSpeedInput: EditText =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_repeat_speed_input)
|
||||||
|
private val speedMinusButton: Button =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_speed_minus_button)
|
||||||
|
private val speedPlusButton: Button =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_speed_plus_button)
|
||||||
|
private val durationInput: EditText =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_long_press_duration_input)
|
||||||
|
private val durationMinusButton: Button =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_duration_minus_button)
|
||||||
|
private val durationPlusButton: Button =
|
||||||
|
sectionView.findViewById(R.id.side_arrow_duration_plus_button)
|
||||||
|
private val testButton: Button = sectionView.findViewById(R.id.side_arrow_test_button)
|
||||||
|
private val restartSamsungUiButton: Button =
|
||||||
|
sectionView.findViewById(R.id.restart_samsung_ui_button)
|
||||||
|
|
||||||
|
private val decimalSeparator = DecimalFormatSymbols.getInstance().decimalSeparator
|
||||||
|
private var lastValidDurationMs = settingsStore.defaultLongPressDurationMs()
|
||||||
|
private var lastValidRepeatSpeed = NavButtonSettingsStore.DEFAULT_SIDE_ARROW_REPEAT_STEPS_PER_SECOND
|
||||||
|
private var updatingDurationText = false
|
||||||
|
private var updatingRepeatSpeedText = false
|
||||||
|
|
||||||
|
fun bind() {
|
||||||
|
sideArrowsCheckbox.isChecked = settingsStore.isSideArrowsEnabled()
|
||||||
|
bindLongPressActionSpinner()
|
||||||
|
applyRepeatSpeed(settingsStore.getSideArrowRepeatStepsPerSecond(), persist = false)
|
||||||
|
bindRepeatSpeedInput()
|
||||||
|
bindRepeatSpeedButtons()
|
||||||
|
applyDuration(settingsStore.getSideArrowLongPressDurationMs(), persist = false)
|
||||||
|
bindDurationInput()
|
||||||
|
bindDurationButtons()
|
||||||
|
bindTestButton()
|
||||||
|
updateOptionsEnabled()
|
||||||
|
|
||||||
|
sideArrowsCheckbox.setOnCheckedChangeListener { _, isChecked ->
|
||||||
|
settingsStore.setSideArrowsEnabled(isChecked)
|
||||||
|
updateOptionsEnabled()
|
||||||
|
sendBroadcast(
|
||||||
|
Intent(NavButtonSettingsStore.ACTION_SETTINGS_CHANGED).putExtra(
|
||||||
|
NavButtonSettingsStore.EXTRA_SIDE_ARROWS_ENABLED,
|
||||||
|
isChecked,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
restartSamsungUiButton.setOnClickListener {
|
||||||
|
restartSamsungUiButton.isEnabled = false
|
||||||
|
Toast.makeText(
|
||||||
|
this@SettingsActivity,
|
||||||
|
"Restarting One UI Home and SystemUI...",
|
||||||
|
Toast.LENGTH_SHORT,
|
||||||
|
).show()
|
||||||
|
Thread {
|
||||||
|
val result = restartSamsungUiProcesses()
|
||||||
|
handler.post {
|
||||||
|
restartSamsungUiButton.isEnabled = true
|
||||||
|
Toast.makeText(this@SettingsActivity, result, Toast.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
}.start()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindLongPressActionSpinner() {
|
||||||
|
val labels = SideArrowLongPressAction.entries.map { getString(it.labelRes) }
|
||||||
|
val adapter = ArrayAdapter(
|
||||||
|
this@SettingsActivity,
|
||||||
|
R.layout.item_spinner_selected,
|
||||||
|
labels,
|
||||||
|
)
|
||||||
|
adapter.setDropDownViewResource(R.layout.item_spinner_dropdown)
|
||||||
|
longPressActionSpinner.adapter = adapter
|
||||||
|
longPressActionSpinner.setSelection(
|
||||||
|
SideArrowLongPressAction.entries.indexOf(settingsStore.getSideArrowLongPressAction()),
|
||||||
|
false,
|
||||||
|
)
|
||||||
|
longPressActionSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
|
||||||
|
override fun onItemSelected(
|
||||||
|
parent: AdapterView<*>?,
|
||||||
|
view: View?,
|
||||||
|
position: Int,
|
||||||
|
id: Long,
|
||||||
|
) {
|
||||||
|
val action = SideArrowLongPressAction.entries[position]
|
||||||
|
settingsStore.setSideArrowLongPressAction(action)
|
||||||
|
updateRepeatSpeedVisibility(action)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onNothingSelected(parent: AdapterView<*>?) = Unit
|
||||||
|
}
|
||||||
|
updateRepeatSpeedVisibility(settingsStore.getSideArrowLongPressAction())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindRepeatSpeedInput() {
|
||||||
|
repeatSpeedInput.setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
if (!hasFocus) {
|
||||||
|
normalizeTypedRepeatSpeed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repeatSpeedInput.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int,
|
||||||
|
) = Unit
|
||||||
|
|
||||||
|
override fun onTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
before: Int,
|
||||||
|
count: Int,
|
||||||
|
) = Unit
|
||||||
|
|
||||||
|
override fun afterTextChanged(editable: Editable?) {
|
||||||
|
if (updatingRepeatSpeedText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val parsed = parseRepeatSpeed(editable?.toString()) ?: return
|
||||||
|
if (parsed in NavButtonSettingsStore.MIN_SIDE_ARROW_REPEAT_STEPS_PER_SECOND..
|
||||||
|
NavButtonSettingsStore.MAX_SIDE_ARROW_REPEAT_STEPS_PER_SECOND
|
||||||
|
) {
|
||||||
|
lastValidRepeatSpeed = parsed
|
||||||
|
settingsStore.setSideArrowRepeatStepsPerSecond(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindRepeatSpeedButtons() {
|
||||||
|
attachRepeatButton(speedMinusButton) {
|
||||||
|
adjustRepeatSpeed(-NavButtonSettingsStore.SIDE_ARROW_REPEAT_SPEED_STEP)
|
||||||
|
}
|
||||||
|
attachRepeatButton(speedPlusButton) {
|
||||||
|
adjustRepeatSpeed(NavButtonSettingsStore.SIDE_ARROW_REPEAT_SPEED_STEP)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindDurationInput() {
|
||||||
|
durationInput.setOnFocusChangeListener { _, hasFocus ->
|
||||||
|
if (!hasFocus) {
|
||||||
|
normalizeTypedDuration()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
durationInput.addTextChangedListener(object : TextWatcher {
|
||||||
|
override fun beforeTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
count: Int,
|
||||||
|
after: Int,
|
||||||
|
) = Unit
|
||||||
|
|
||||||
|
override fun onTextChanged(
|
||||||
|
s: CharSequence?,
|
||||||
|
start: Int,
|
||||||
|
before: Int,
|
||||||
|
count: Int,
|
||||||
|
) = Unit
|
||||||
|
|
||||||
|
override fun afterTextChanged(editable: Editable?) {
|
||||||
|
if (updatingDurationText) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val parsed = editable?.toString()?.toIntOrNull() ?: return
|
||||||
|
if (parsed in NavButtonSettingsStore.MIN_DURATION_MS..NavButtonSettingsStore.MAX_DURATION_MS) {
|
||||||
|
lastValidDurationMs = parsed
|
||||||
|
settingsStore.setSideArrowLongPressDurationMs(parsed)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindDurationButtons() {
|
||||||
|
attachRepeatButton(durationMinusButton) {
|
||||||
|
adjustDuration(-NavButtonSettingsStore.DURATION_STEP_MS)
|
||||||
|
}
|
||||||
|
attachRepeatButton(durationPlusButton) {
|
||||||
|
adjustDuration(NavButtonSettingsStore.DURATION_STEP_MS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun bindTestButton() {
|
||||||
|
testButton.setOnTouchListener { view, event ->
|
||||||
|
when (event.actionMasked) {
|
||||||
|
MotionEvent.ACTION_DOWN -> {
|
||||||
|
view.isPressed = true
|
||||||
|
startTestPreview(resolveDurationForUse())
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
MotionEvent.ACTION_UP,
|
||||||
|
MotionEvent.ACTION_CANCEL,
|
||||||
|
-> {
|
||||||
|
view.isPressed = false
|
||||||
|
stopTestPreview()
|
||||||
|
true
|
||||||
|
}
|
||||||
|
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateOptionsEnabled() {
|
||||||
|
val enabled = sideArrowsCheckbox.isChecked
|
||||||
|
optionsContainer.alpha = if (enabled) 1f else 0.45f
|
||||||
|
setEnabledRecursively(optionsContainer, enabled)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun setEnabledRecursively(view: View, enabled: Boolean) {
|
||||||
|
view.isEnabled = enabled
|
||||||
|
if (view is ViewGroup) {
|
||||||
|
repeat(view.childCount) { index ->
|
||||||
|
setEnabledRecursively(view.getChildAt(index), enabled)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun updateRepeatSpeedVisibility(action: SideArrowLongPressAction) {
|
||||||
|
repeatSpeedContainer.visibility =
|
||||||
|
if (action == SideArrowLongPressAction.MULTIPLE_STEPS) View.VISIBLE else View.GONE
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun adjustRepeatSpeed(delta: Float) {
|
||||||
|
applyRepeatSpeed(resolveRepeatSpeedForUse() + delta, persist = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalizeTypedRepeatSpeed(): Float {
|
||||||
|
val parsed = parseRepeatSpeed(repeatSpeedInput.text.toString()) ?: lastValidRepeatSpeed
|
||||||
|
val normalized = settingsStore.sanitizeSideArrowRepeatSpeed(parsed)
|
||||||
|
applyRepeatSpeed(normalized, persist = true)
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveRepeatSpeedForUse(): Float {
|
||||||
|
return normalizeTypedRepeatSpeed()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyRepeatSpeed(stepsPerSecond: Float, persist: Boolean) {
|
||||||
|
val sanitized = settingsStore.sanitizeSideArrowRepeatSpeed(stepsPerSecond)
|
||||||
|
val text = formatRepeatSpeed(sanitized)
|
||||||
|
updatingRepeatSpeedText = true
|
||||||
|
if (repeatSpeedInput.text.toString() != text) {
|
||||||
|
repeatSpeedInput.setText(text)
|
||||||
|
repeatSpeedInput.setSelection(repeatSpeedInput.text.length)
|
||||||
|
}
|
||||||
|
updatingRepeatSpeedText = false
|
||||||
|
|
||||||
|
lastValidRepeatSpeed = sanitized
|
||||||
|
if (persist) {
|
||||||
|
settingsStore.setSideArrowRepeatStepsPerSecond(sanitized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun parseRepeatSpeed(text: String?): Float? {
|
||||||
|
return text
|
||||||
|
?.trim()
|
||||||
|
?.replace(',', '.')
|
||||||
|
?.toFloatOrNull()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun formatRepeatSpeed(stepsPerSecond: Float): String {
|
||||||
|
val raw = if (stepsPerSecond == stepsPerSecond.toInt().toFloat()) {
|
||||||
|
stepsPerSecond.toInt().toString()
|
||||||
|
} else {
|
||||||
|
String.format(Locale.US, "%.2f", stepsPerSecond)
|
||||||
|
.trimEnd('0')
|
||||||
|
.trimEnd('.')
|
||||||
|
}
|
||||||
|
return raw.replace('.', decimalSeparator)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun adjustDuration(deltaMs: Int) {
|
||||||
|
val current = resolveDurationForUse()
|
||||||
|
val updated = (current + deltaMs).coerceIn(
|
||||||
|
NavButtonSettingsStore.MIN_DURATION_MS,
|
||||||
|
NavButtonSettingsStore.MAX_DURATION_MS,
|
||||||
|
)
|
||||||
|
applyDuration(updated, persist = true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun normalizeTypedDuration(): Int {
|
||||||
|
val parsed = durationInput.text.toString().toIntOrNull() ?: lastValidDurationMs
|
||||||
|
val normalized = settingsStore.sanitizeDuration(parsed)
|
||||||
|
applyDuration(normalized, persist = true)
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveDurationForUse(): Int {
|
||||||
|
return normalizeTypedDuration()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun applyDuration(durationMs: Int, persist: Boolean) {
|
||||||
|
val sanitized = settingsStore.sanitizeDuration(durationMs)
|
||||||
|
val text = sanitized.toString()
|
||||||
|
updatingDurationText = true
|
||||||
|
if (durationInput.text.toString() != text) {
|
||||||
|
durationInput.setText(text)
|
||||||
|
durationInput.setSelection(durationInput.text.length)
|
||||||
|
}
|
||||||
|
updatingDurationText = false
|
||||||
|
|
||||||
|
lastValidDurationMs = sanitized
|
||||||
|
if (persist) {
|
||||||
|
settingsStore.setSideArrowLongPressDurationMs(sanitized)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun restartSamsungUiProcesses(): String {
|
||||||
|
return try {
|
||||||
|
val command = listOf(
|
||||||
|
"am force-stop com.sec.android.app.launcher",
|
||||||
|
"killall com.android.systemui",
|
||||||
|
"sleep 1",
|
||||||
|
"am start -a android.intent.action.MAIN -c android.intent.category.HOME",
|
||||||
|
).joinToString("; ")
|
||||||
|
val process = Runtime.getRuntime().exec(arrayOf("su", "-c", command))
|
||||||
|
val finished = process.waitFor(10, TimeUnit.SECONDS)
|
||||||
|
if (!finished) {
|
||||||
|
process.destroyForcibly()
|
||||||
|
"Restart command timed out."
|
||||||
|
} else if (process.exitValue() == 0) {
|
||||||
|
"Restart command completed."
|
||||||
|
} else {
|
||||||
|
"Restart command failed. Is root allowed?"
|
||||||
|
}
|
||||||
|
} catch (_: Throwable) {
|
||||||
|
"Restart command failed. Is root allowed?"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -29,15 +29,6 @@
|
|||||||
android:textSize="30sp"
|
android:textSize="30sp"
|
||||||
android:textStyle="bold" />
|
android:textStyle="bold" />
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_marginTop="8dp"
|
|
||||||
android:lineSpacingExtra="3dp"
|
|
||||||
android:text="@string/settings_summary"
|
|
||||||
android:textColor="@color/settings_text_secondary"
|
|
||||||
android:textSize="15sp" />
|
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:id="@+id/button_sections_container"
|
android:id="@+id/button_sections_container"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
|
|||||||
@@ -0,0 +1,186 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:background="@drawable/bg_button_section"
|
||||||
|
android:elevation="2dp"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:padding="18dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/side_arrows_section_title"
|
||||||
|
android:textColor="@color/settings_text_primary"
|
||||||
|
android:textSize="22sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<CheckBox
|
||||||
|
android:id="@+id/side_arrows_checkbox"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:buttonTint="@color/settings_label_text"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:text="@string/side_arrows_enable_label"
|
||||||
|
android:textColor="@color/settings_text_primary"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/side_arrows_options_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/long_press_action_label"
|
||||||
|
android:textColor="@color/settings_label_text"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<Spinner
|
||||||
|
android:id="@+id/side_arrow_long_press_action_spinner"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="6dp"
|
||||||
|
android:background="@drawable/bg_spinner_field"
|
||||||
|
android:dropDownWidth="match_parent"
|
||||||
|
android:paddingStart="12dp"
|
||||||
|
android:paddingTop="6dp"
|
||||||
|
android:paddingEnd="12dp"
|
||||||
|
android:paddingBottom="6dp"
|
||||||
|
android:popupBackground="@drawable/bg_spinner_popup"
|
||||||
|
android:saveEnabled="false" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:id="@+id/side_arrow_repeat_speed_container"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="@string/side_arrow_repeat_speed_label"
|
||||||
|
android:textColor="@color/settings_label_text"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/side_arrow_speed_minus_button"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:text="@string/minus_button_label"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="22sp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/side_arrow_repeat_speed_input"
|
||||||
|
android:layout_width="88dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="numberDecimal"
|
||||||
|
android:maxLength="6"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/settings_input_text"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/side_arrow_speed_plus_button"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:text="@string/plus_button_label"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="16dp"
|
||||||
|
android:text="@string/long_press_duration_label"
|
||||||
|
android:textColor="@color/settings_label_text"
|
||||||
|
android:textSize="13sp"
|
||||||
|
android:textStyle="bold" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="8dp"
|
||||||
|
android:gravity="center_vertical"
|
||||||
|
android:orientation="horizontal">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/side_arrow_duration_minus_button"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:text="@string/minus_button_label"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="22sp" />
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/side_arrow_long_press_duration_input"
|
||||||
|
android:layout_width="88dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:gravity="center"
|
||||||
|
android:importantForAutofill="no"
|
||||||
|
android:inputType="number"
|
||||||
|
android:maxLength="5"
|
||||||
|
android:saveEnabled="false"
|
||||||
|
android:singleLine="true"
|
||||||
|
android:textColor="@color/settings_input_text"
|
||||||
|
android:textSize="18sp" />
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/side_arrow_duration_plus_button"
|
||||||
|
android:layout_width="48dp"
|
||||||
|
android:layout_height="48dp"
|
||||||
|
android:layout_marginStart="8dp"
|
||||||
|
android:minWidth="48dp"
|
||||||
|
android:text="@string/plus_button_label"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="20sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/side_arrow_test_button"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="54dp"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:minWidth="160dp"
|
||||||
|
android:text="@string/test_button_label"
|
||||||
|
android:textAllCaps="false"
|
||||||
|
android:textSize="16sp" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/restart_samsung_ui_button"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="14dp"
|
||||||
|
android:text="@string/restart_samsung_ui_button_label" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
@@ -2,10 +2,16 @@
|
|||||||
<string name="app_name">NavButtons</string>
|
<string name="app_name">NavButtons</string>
|
||||||
<string name="xposed_desc">Customize navigation button press and long-press actions.</string>
|
<string name="xposed_desc">Customize navigation button press and long-press actions.</string>
|
||||||
<string name="settings_title">Nav Button Settings</string>
|
<string name="settings_title">Nav Button Settings</string>
|
||||||
<string name="settings_summary">Choose what each navigation button should do on press and long press, then tune how long a long press should feel.</string>
|
|
||||||
<string name="button_back">Back Button</string>
|
<string name="button_back">Back Button</string>
|
||||||
<string name="button_home">Home Button</string>
|
<string name="button_home">Home Button</string>
|
||||||
<string name="button_recents">Recents Button</string>
|
<string name="button_recents">Recents Button</string>
|
||||||
|
<string name="side_arrows_section_title">Side arrows</string>
|
||||||
|
<string name="side_arrows_enable_label">Enable side arrows</string>
|
||||||
|
<string name="side_arrow_long_press_nothing">Nothing</string>
|
||||||
|
<string name="side_arrow_long_press_single_step">Single step</string>
|
||||||
|
<string name="side_arrow_long_press_multiple_steps">Multiple steps</string>
|
||||||
|
<string name="side_arrow_repeat_speed_label">Repeat speed (steps/sec)</string>
|
||||||
|
<string name="restart_samsung_ui_button_label">Restart One UI Home + SystemUI</string>
|
||||||
<string name="press_action_label">When pressed</string>
|
<string name="press_action_label">When pressed</string>
|
||||||
<string name="long_press_action_label">When long-pressed</string>
|
<string name="long_press_action_label">When long-pressed</string>
|
||||||
<string name="long_press_duration_label">Long-press duration (ms)</string>
|
<string name="long_press_duration_label">Long-press duration (ms)</string>
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
android
|
android
|
||||||
com.android.systemui
|
com.android.systemui
|
||||||
com.sec.android.app.launcher
|
com.sec.android.app.launcher
|
||||||
|
com.samsung.systemui.navillera
|
||||||
|
|||||||
Reference in New Issue
Block a user