Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
2a023c3
add simple key map fields comparators
maksimowiczm Nov 9, 2024
ff05ca8
add sorting use cases
maksimowiczm Nov 9, 2024
4b04437
use sorting inside `ListKeyMapsUseCaseImpl`
maksimowiczm Nov 9, 2024
11e736f
add ui
maksimowiczm Nov 9, 2024
5e9daa7
rename `KeyMapField` to `SortField`
maksimowiczm Nov 17, 2024
011f7ab
Implement `KeyMapConstraintsComparator`
maksimowiczm Nov 17, 2024
81dd15f
Implement `KeyMapTriggerComparator`
maksimowiczm Nov 17, 2024
635e7c2
fix constraints sorting
maksimowiczm Nov 25, 2024
58ce5e0
add sorting actions by value
maksimowiczm Nov 25, 2024
0640365
add sorting by options
maksimowiczm Nov 25, 2024
559ac8b
fix menu
maksimowiczm Nov 26, 2024
e7b759f
add `None` sort order
maksimowiczm Nov 26, 2024
9ba565c
implement sort menu with chips
maksimowiczm Nov 26, 2024
dea3889
refactor use cases associated with `SortField` priority
maksimowiczm Dec 19, 2024
0a191fe
add chip drag
maksimowiczm Dec 19, 2024
ad9a41d
Merge remote-tracking branch 'prod/develop' into feature/sorting
maksimowiczm Dec 19, 2024
1127a13
add compose ui
maksimowiczm Dec 23, 2024
f38ad92
go back to string preference because somehow set stopped working
maksimowiczm Dec 23, 2024
61e567a
simplify use cases
maksimowiczm Dec 23, 2024
cce4ef0
that wasn't supposed to be here
maksimowiczm Dec 23, 2024
bb2b354
fix sort cancel
maksimowiczm Dec 26, 2024
133bb3d
move comparators to sorting package
maksimowiczm Dec 26, 2024
2a317d8
another ui version goes brrr
maksimowiczm Dec 26, 2024
a8f8123
use material3 modal bottom sheet
maksimowiczm Dec 26, 2024
71143e8
improve sort help
maksimowiczm Dec 27, 2024
9b6cd73
clean up
maksimowiczm Dec 28, 2024
549edcf
squash use cases
maksimowiczm Dec 28, 2024
87f8298
fix bottom sheet lag
maksimowiczm Dec 28, 2024
0a28629
fix: hide the drag handle on Advanced triggers bottom sheet to be con…
sds100 Dec 29, 2024
5ddba58
#1337 refactor, fix style, and remove unnecessary code
sds100 Dec 29, 2024
f665299
#1337 rename doFinal method
sds100 Dec 29, 2024
effc42e
#1337 remove sorting/ui package
sds100 Dec 29, 2024
ed433fb
#1337 use interface for SortKeyMapsUseCase
sds100 Dec 29, 2024
f05070d
#1337 update comments
sds100 Dec 29, 2024
19e47d4
Merge branch 'develop' into feature/sorting
sds100 Dec 29, 2024
37f466b
Merge branch 'develop' into feature/sorting
sds100 Dec 29, 2024
7d8fb55
use localized strings for constraints comparison
maksimowiczm Jan 5, 2025
9070e83
Merge branch 'develop' into feature/sorting
sds100 Jan 22, 2025
5eb072b
#1223 remove unnecessary functions for creating comparators
sds100 Jan 22, 2025
7833710
#1223 show reset sort button when changing the order as well
sds100 Feb 2, 2025
7f2bfe4
#1223 sort constraints by their type and sub fields if there are any
sds100 Feb 2, 2025
0898aa9
#1223 sort actions by their type and sub fields if there are any
sds100 Feb 2, 2025
747da9d
Merge branch 'develop' into feature/sorting
sds100 Feb 2, 2025
c955c5c
upload apk artifact from pull request github actions
sds100 Feb 2, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions app/src/main/java/io/github/sds100/keymapper/UseCases.kt
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,11 @@ import io.github.sds100.keymapper.mappings.keymaps.detection.DetectKeyMapsUseCas
import io.github.sds100.keymapper.onboarding.OnboardingUseCaseImpl
import io.github.sds100.keymapper.reroutekeyevents.RerouteKeyEventsUseCaseImpl
import io.github.sds100.keymapper.shizuku.ShizukuInputEventInjector
import io.github.sds100.keymapper.sorting.ObserveKeyMapFieldSortOrderUseCase
import io.github.sds100.keymapper.sorting.ObserveKeyMapSortOrderUseCase
import io.github.sds100.keymapper.sorting.ObserveKeyMapsSorterUseCase
import io.github.sds100.keymapper.sorting.SetKeyMapSortOrderUseCase
import io.github.sds100.keymapper.sorting.ToggleKeyMapFieldSortOrderUseCase
import io.github.sds100.keymapper.system.Shell
import io.github.sds100.keymapper.system.accessibility.ControlAccessibilityServiceUseCase
import io.github.sds100.keymapper.system.accessibility.ControlAccessibilityServiceUseCaseImpl
Expand Down Expand Up @@ -231,4 +236,25 @@ object UseCases {
keyEventRelayService,
ServiceLocator.inputMethodAdapter(ctx),
)

fun observeKeyMapFieldSortOrderUseCase(ctx: Context) = ObserveKeyMapFieldSortOrderUseCase(
ServiceLocator.settingsRepository(ctx),
)

fun observeKeyMapsSorter(ctx: Context) = ObserveKeyMapsSorterUseCase(
observeKeyMapFieldSortOrderUseCase(ctx),
observeKeyMapSortOrderUseCase(ctx),
)

fun setKeyMapFieldSortOrderUseCase(ctx: Context) = ToggleKeyMapFieldSortOrderUseCase(
ServiceLocator.settingsRepository(ctx),
)

fun observeKeyMapSortOrderUseCase(ctx: Context) = ObserveKeyMapSortOrderUseCase(
ServiceLocator.settingsRepository(ctx),
)

fun setKeyMapSortOrderUseCase(ctx: Context) = SetKeyMapSortOrderUseCase(
ServiceLocator.settingsRepository(ctx),
)
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package io.github.sds100.keymapper.constraints

import io.github.sds100.keymapper.mappings.keymaps.KeyMap
import kotlinx.serialization.Serializable

/**
Expand All @@ -11,3 +12,41 @@ data class ConstraintState(
val constraints: Set<Constraint> = emptySet(),
val mode: ConstraintMode = ConstraintMode.AND,
)

// Comparator.reversed() requires API level 24
class KeyMapConstraintsComparator(
private val reverse: Boolean = false,
) : Comparator<KeyMap> {
override fun compare(
keyMap: KeyMap?,
otherKeyMap: KeyMap?,
): Int {
// TODO

if (keyMap == null || otherKeyMap == null) {
return 0
}

for (i in 0 until keyMap.constraintState.constraints.size.coerceAtMost(otherKeyMap.constraintState.constraints.size)) {
val constraint1 = keyMap.constraintState.constraints.elementAt(i)
val constraint2 = otherKeyMap.constraintState.constraints.elementAt(i)

val result = constraint1.javaClass.name.compareTo(constraint2.javaClass.name)

if (result != 0) {
return doFinal(result)
}
}

val comparison =
keyMap.constraintState.constraints.size.compareTo(otherKeyMap.constraintState.constraints.size)

return doFinal(comparison)
}

fun doFinal(result: Int) = if (reverse) {
result * -1
} else {
result
}
}
7 changes: 7 additions & 0 deletions app/src/main/java/io/github/sds100/keymapper/data/Keys.kt
Original file line number Diff line number Diff line change
Expand Up @@ -70,4 +70,11 @@ object Keys {
val savedWifiSSIDs = stringSetPreferencesKey("key_saved_wifi_ssids")

val neverShowDndError = booleanPreferencesKey("key_never_show_dnd_error")

// true = ascending, false = descending
val sortTriggerAscending = booleanPreferencesKey("key_sort_trigger_ascending")
val sortActionsAscending = booleanPreferencesKey("key_sort_actions_ascending")
val sortConstraintsAscending = booleanPreferencesKey("key_sort_constraints_ascending")
val sortOptionsAscending = booleanPreferencesKey("key_sort_options_ascending")
val sortOrder = stringPreferencesKey("key_sort_order")
}
13 changes: 13 additions & 0 deletions app/src/main/java/io/github/sds100/keymapper/home/HomeFragment.kt
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,19 @@ class HomeFragment : Fragment() {
backupFingerprintMapsLauncher.launch(BackupUtils.createFingerprintMapsFileName())
}
}

viewLifecycleOwner.launchRepeatOnLifecycle(Lifecycle.State.RESUMED) {
binding.appBar.setOnMenuItemClickListener {
when (it.itemId) {
R.id.action_sort -> {
findNavController().navigate(R.id.action_global_sortingFragment)
true
}

else -> false
}
}
}
}

override fun onDestroyView() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -106,3 +106,37 @@ object KeyMapEntityMapper {
)
}
}

// Comparator.reversed() requires API level 24
class KeyMapOptionsComparator(
private val reverse: Boolean = false,
) : Comparator<KeyMap> {
override fun compare(
keyMap: KeyMap?,
otherKeyMap: KeyMap?,
): Int {
// TODO

if (keyMap == null || otherKeyMap == null) {
return 0
}

// Compare the options

if (keyMap.vibrate != otherKeyMap.vibrate) {
return doFinal(keyMap.vibrate.compareTo(otherKeyMap.vibrate))
}

if (keyMap.showToast != otherKeyMap.showToast) {
return doFinal(keyMap.showToast.compareTo(otherKeyMap.showToast))
}

return 0
}

fun doFinal(result: Int) = if (reverse) {
result * -1
} else {
result
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -200,3 +200,39 @@ object KeymapActionEntityMapper {
)
}
}

// Comparator.reversed() requires API level 24
class KeyMapActionsComparator(
private val reverse: Boolean = false,
) : Comparator<KeyMap> {
override fun compare(
keyMap: KeyMap?,
otherKeyMap: KeyMap?,
): Int {
// TODO
if (keyMap == null || otherKeyMap == null) {
return 0
}

for (i in 0 until keyMap.actionList.size.coerceAtMost(otherKeyMap.actionList.size)) {
val action1 = keyMap.actionList[i]
val action2 = otherKeyMap.actionList[i]

val result = action1.data.id.ordinal.compareTo(action2.data.id.ordinal)

if (result != 0) {
return doFinal(result)
}
}

val comparison = keyMap.actionList.size.compareTo(otherKeyMap.actionList.size)

return doFinal(comparison)
}

fun doFinal(result: Int) = if (reverse) {
result * -1
} else {
result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package io.github.sds100.keymapper.mappings.keymaps

import io.github.sds100.keymapper.constraints.KeyMapConstraintsComparator
import io.github.sds100.keymapper.mappings.keymaps.trigger.KeyMapTriggerComparator

enum class KeyMapField {
TRIGGER,
ACTIONS,
CONSTRAINTS,
OPTIONS,
;

fun getComparator(reverse: Boolean) = when (this) {
TRIGGER -> KeyMapTriggerComparator(reverse)
ACTIONS -> KeyMapActionsComparator(reverse)
CONSTRAINTS -> KeyMapConstraintsComparator(reverse)
OPTIONS -> KeyMapOptionsComparator(reverse)
}
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
package io.github.sds100.keymapper.mappings.keymaps

import io.github.sds100.keymapper.backup.BackupManager
import io.github.sds100.keymapper.sorting.ObserveKeyMapsSorterUseCase
import io.github.sds100.keymapper.util.Result
import io.github.sds100.keymapper.util.State
import io.github.sds100.keymapper.util.mapData
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.channelFlow
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.withContext

/**
Expand All @@ -17,23 +19,25 @@ class ListKeyMapsUseCaseImpl(
private val keyMapRepository: KeyMapRepository,
private val backupManager: BackupManager,
displayKeyMapUseCase: DisplayKeyMapUseCase,
private val observeKeyMapsSorterUseCase: ObserveKeyMapsSorterUseCase,
) : ListKeyMapsUseCase,
DisplayKeyMapUseCase by displayKeyMapUseCase {

override val keyMapList: Flow<State<List<KeyMap>>> = channelFlow {
send(State.Loading)

keyMapRepository.keyMapList.collectLatest { keyMapEntitiesState ->
send(State.Loading)

combine(
keyMapRepository.keyMapList,
observeKeyMapsSorterUseCase(),
) { keyMapEntitiesState, sorter ->
keyMapEntitiesState.mapData { keyMapEntities ->
keyMapEntities
.map { KeyMapEntityMapper.fromEntity(it) }
.sortedWith(sorter)
}
}.collectLatest {
withContext(Dispatchers.Default) {
val keyMaps = keyMapEntitiesState.mapData { keyMapEntities ->
keyMapEntities.map {
KeyMapEntityMapper.fromEntity(it)
}
}

send(keyMaps)
send(it)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import io.github.sds100.keymapper.data.entities.Extra
import io.github.sds100.keymapper.data.entities.TriggerEntity
import io.github.sds100.keymapper.data.entities.getData
import io.github.sds100.keymapper.mappings.ClickType
import io.github.sds100.keymapper.mappings.keymaps.KeyMap
import io.github.sds100.keymapper.system.keyevents.KeyEventUtils
import io.github.sds100.keymapper.util.valueOrNull
import kotlinx.serialization.Serializable
Expand Down Expand Up @@ -165,3 +166,28 @@ object KeymapTriggerEntityMapper {
)
}
}

// Comparator.reversed() requires API level 24
class KeyMapTriggerComparator(
private val reverse: Boolean = false,
) : Comparator<KeyMap> {
override fun compare(
keyMap: KeyMap?,
otherKeyMap: KeyMap?,
): Int {
// TODO
if (keyMap == null || otherKeyMap == null) {
return 0
}

val result = 0

return doFinal(result)
}

fun doFinal(result: Int) = if (reverse) {
result * -1
} else {
result
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package io.github.sds100.keymapper.sorting

import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.mappings.keymaps.KeyMapField
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
* Observes the sorting order of a key map field, indicating the order in which the specified field
* should be sorted.
*/
class ObserveKeyMapFieldSortOrderUseCase(
private val preferenceRepository: PreferenceRepository,
) {
/**
* Observe the sorting order of the key map field.
*
* @param field The key map field to observe.
* @return A flow of the sorting order.
*/
operator fun invoke(field: KeyMapField): Flow<SortOrder> {
val key = when (field) {
KeyMapField.TRIGGER -> Keys.sortTriggerAscending
KeyMapField.ACTIONS -> Keys.sortActionsAscending
KeyMapField.CONSTRAINTS -> Keys.sortConstraintsAscending
KeyMapField.OPTIONS -> Keys.sortOptionsAscending
}

return preferenceRepository.get(key).map {
when (it) {
// Default value is ascending
null, true -> SortOrder.ASCENDING
false -> SortOrder.DESCENDING
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
package io.github.sds100.keymapper.sorting

import io.github.sds100.keymapper.data.Keys
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
import io.github.sds100.keymapper.mappings.keymaps.KeyMapField
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.map

/**
* Observes the order in which key map fields should be sorted, prioritizing specific fields.
* For example, if the order is [TRIGGER, ACTIONS, CONSTRAINTS, OPTIONS],
* it means the key maps should be sorted first by trigger, then by actions, followed by constraints,
* and finally by options.
*/
class ObserveKeyMapSortOrderUseCase(
private val preferenceRepository: PreferenceRepository,
) {
private val default by lazy {
listOf(
KeyMapField.TRIGGER,
KeyMapField.ACTIONS,
KeyMapField.CONSTRAINTS,
KeyMapField.OPTIONS,
)
}

operator fun invoke(): Flow<List<KeyMapField>> {
return preferenceRepository
.get(Keys.sortOrder)
.map {
val result = runCatching {
it
?.split(",")
?.map { KeyMapField.valueOf(it) }
?: default
}.getOrDefault(default).distinct()

// If the result is not the expected size it means that the preference is corrupted
if (result.size != 4) {
return@map default
}

result
}
}
}
Loading