Skip to content

Commit b8f32da

Browse files
authored
Merge branch 'develop' into copilot/refactor-volume-actions
2 parents 3a6ac16 + 1498541 commit b8f32da

File tree

14 files changed

+206
-20
lines changed

14 files changed

+206
-20
lines changed

app/version.properties

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
VERSION_NAME=4.0.0-beta.1
2-
VERSION_CODE=175
2+
VERSION_CODE=176
33
VERSION_NUM=01

base/src/main/java/io/github/sds100/keymapper/base/actions/ActionsScreen.kt

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,8 @@ import io.github.sds100.keymapper.base.actions.keyevent.FixKeyEventActionBottomS
4242
import io.github.sds100.keymapper.base.compose.KeyMapperTheme
4343
import io.github.sds100.keymapper.base.keymaps.ShortcutModel
4444
import io.github.sds100.keymapper.base.keymaps.ShortcutRow
45+
import io.github.sds100.keymapper.base.onboarding.OnboardingTipModel
46+
import io.github.sds100.keymapper.base.onboarding.TipCard
4547
import io.github.sds100.keymapper.base.utils.ui.LinkType
4648
import io.github.sds100.keymapper.base.utils.ui.compose.ComposeIconInfo
4749
import io.github.sds100.keymapper.base.utils.ui.compose.DraggableItem
@@ -56,6 +58,7 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod
5658
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
5759
val state by viewModel.state.collectAsStateWithLifecycle()
5860
val optionsState by viewModel.actionOptionsState.collectAsStateWithLifecycle()
61+
val actionTipModel by viewModel.actionsTip.collectAsStateWithLifecycle()
5962

6063
if (optionsState != null) {
6164
ActionOptionsBottomSheet(
@@ -91,27 +94,33 @@ fun ActionsScreen(modifier: Modifier = Modifier, viewModel: ConfigActionsViewMod
9194
ActionsScreen(
9295
modifier = modifier,
9396
state = state,
97+
tipModel = actionTipModel,
9498
onRemoveClick = viewModel::onRemoveClick,
9599
onEditClick = viewModel::onEditClick,
96100
onMoveAction = viewModel::onMoveAction,
97101
onFixErrorClick = viewModel::onFixError,
98102
onClickShortcut = viewModel::onClickShortcut,
99103
onTestClick = viewModel::onTestClick,
100104
onAddClick = viewModel::onAddActionClick,
105+
onActionTipDismiss = viewModel::onActionTipDismissClick,
106+
onTipButtonClick = viewModel::onTipButtonClick,
101107
)
102108
}
103109

104110
@Composable
105111
private fun ActionsScreen(
106112
modifier: Modifier = Modifier,
107113
state: State<ConfigActionsState>,
114+
tipModel: OnboardingTipModel? = null,
108115
onAddClick: () -> Unit = {},
109116
onRemoveClick: (String) -> Unit = {},
110117
onEditClick: (String) -> Unit = {},
111118
onMoveAction: (fromIndex: Int, toIndex: Int) -> Unit = { _, _ -> },
112119
onFixErrorClick: (String) -> Unit = {},
113120
onTestClick: (String) -> Unit = {},
114121
onClickShortcut: (ActionData) -> Unit = {},
122+
onActionTipDismiss: () -> Unit = {},
123+
onTipButtonClick: (String) -> Unit = {},
115124
) {
116125
var showDeleteDialog by rememberSaveable { mutableStateOf(false) }
117126
var actionToDelete by rememberSaveable { mutableStateOf<String?>(null) }
@@ -143,6 +152,25 @@ private fun ActionsScreen(
143152
State.Loading -> Loading()
144153
is State.Data<ConfigActionsState> -> Surface(modifier = modifier) {
145154
Column {
155+
Spacer(Modifier.height(8.dp))
156+
157+
// Display action tip if available
158+
tipModel?.let { tip ->
159+
TipCard(
160+
modifier = Modifier
161+
.fillMaxWidth()
162+
.padding(horizontal = 16.dp),
163+
title = tip.title,
164+
message = tip.message,
165+
isDismissable = tip.isDismissable,
166+
onDismiss = onActionTipDismiss,
167+
buttonText = tip.buttonText,
168+
onButtonClick = { onTipButtonClick(tip.id) },
169+
)
170+
171+
Spacer(Modifier.height(8.dp))
172+
}
173+
146174
when (val data = state.data) {
147175
is ConfigActionsState.Empty -> {
148176
Column(
@@ -178,8 +206,6 @@ private fun ActionsScreen(
178206
}
179207

180208
is ConfigActionsState.Loaded -> {
181-
Spacer(Modifier.height(8.dp))
182-
183209
if (data.actions.isNotEmpty()) {
184210
Spacer(Modifier.height(8.dp))
185211

base/src/main/java/io/github/sds100/keymapper/base/actions/ConfigActionsViewModel.kt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import io.github.sds100.keymapper.base.R
77
import io.github.sds100.keymapper.base.actions.keyevent.FixKeyEventActionDelegate
88
import io.github.sds100.keymapper.base.keymaps.KeyMap
99
import io.github.sds100.keymapper.base.keymaps.ShortcutModel
10+
import io.github.sds100.keymapper.base.onboarding.OnboardingTipDelegate
1011
import io.github.sds100.keymapper.base.onboarding.SetupAccessibilityServiceDelegate
1112
import io.github.sds100.keymapper.base.utils.getFullMessage
1213
import io.github.sds100.keymapper.base.utils.isFixable
@@ -48,6 +49,7 @@ class ConfigActionsViewModel @Inject constructor(
4849
private val config: ConfigActionsUseCase,
4950
setupAccessibilityServiceDelegate: SetupAccessibilityServiceDelegate,
5051
fixKeyEventActionDelegate: FixKeyEventActionDelegate,
52+
onboardingTipDelegate: OnboardingTipDelegate,
5153
resourceProvider: ResourceProvider,
5254
navigationProvider: NavigationProvider,
5355
dialogProvider: DialogProvider,
@@ -57,7 +59,8 @@ class ConfigActionsViewModel @Inject constructor(
5759
ResourceProvider by resourceProvider,
5860
DialogProvider by dialogProvider,
5961
NavigationProvider by navigationProvider,
60-
FixKeyEventActionDelegate by fixKeyEventActionDelegate {
62+
FixKeyEventActionDelegate by fixKeyEventActionDelegate,
63+
OnboardingTipDelegate by onboardingTipDelegate {
6164

6265
val createActionDelegate =
6366
CreateActionDelegate(viewModelScope, createAction, this, this, this)

base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipDelegate.kt

Lines changed: 77 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,21 @@
11
package io.github.sds100.keymapper.base.onboarding
22

3+
import android.os.Build
34
import dagger.hilt.android.scopes.ViewModelScoped
45
import io.github.sds100.keymapper.base.R
5-
import io.github.sds100.keymapper.base.keymaps.DisplayKeyMapUseCase
6+
import io.github.sds100.keymapper.base.actions.Action
7+
import io.github.sds100.keymapper.base.actions.ActionData
8+
import io.github.sds100.keymapper.base.actions.ConfigActionsUseCase
69
import io.github.sds100.keymapper.base.trigger.ConfigTriggerUseCase
710
import io.github.sds100.keymapper.base.trigger.KeyCodeTriggerKey
811
import io.github.sds100.keymapper.base.trigger.KeyEventTriggerKey
912
import io.github.sds100.keymapper.base.trigger.Trigger
1013
import io.github.sds100.keymapper.base.trigger.TriggerMode
14+
import io.github.sds100.keymapper.base.utils.navigation.NavDestination
15+
import io.github.sds100.keymapper.base.utils.navigation.NavigationProvider
16+
import io.github.sds100.keymapper.base.utils.navigation.navigate
1117
import io.github.sds100.keymapper.base.utils.ui.ResourceProvider
18+
import io.github.sds100.keymapper.common.utils.Constants
1219
import io.github.sds100.keymapper.common.utils.dataOrNull
1320
import io.github.sds100.keymapper.data.Keys
1421
import io.github.sds100.keymapper.data.repositories.PreferenceRepository
@@ -28,9 +35,11 @@ class OnboardingTipDelegateImpl @Inject constructor(
2835
private val viewModelScope: CoroutineScope,
2936
private val preferenceRepository: PreferenceRepository,
3037
private val configTriggerUseCase: ConfigTriggerUseCase,
31-
private val displayKeyMap: DisplayKeyMapUseCase,
38+
private val configActionsUseCase: ConfigActionsUseCase,
3239
resourceProvider: ResourceProvider,
40+
navigationProvider: NavigationProvider
3341
) : OnboardingTipDelegate,
42+
NavigationProvider by navigationProvider,
3443
PreferenceRepository by preferenceRepository,
3544
ResourceProvider by resourceProvider {
3645

@@ -42,9 +51,11 @@ class OnboardingTipDelegateImpl @Inject constructor(
4251
const val CAPS_LOCK_TIP_ID = "caps_lock_tip"
4352
const val SCREEN_PINNING_TIP_ID = "screen_pinning_tip"
4453
const val IME_DETECTION_TIP_ID = "ime_detection_tip"
54+
const val RINGER_MODE_TIP_ID = "ringer_mode_tip"
4555
}
4656

4757
override val triggerTip: MutableStateFlow<OnboardingTipModel?> = MutableStateFlow(null)
58+
override val actionsTip: MutableStateFlow<OnboardingTipModel?> = MutableStateFlow(null)
4859

4960
private var shownParallelTriggerOrderExplanation: Boolean by PrefDelegate(
5061
Keys.shownParallelTriggerOrderExplanation,
@@ -76,6 +87,11 @@ class OnboardingTipDelegateImpl @Inject constructor(
7687
false,
7788
)
7889

90+
private var shownRingerModeTip: Boolean by PrefDelegate(
91+
Keys.shownRingerModeTip,
92+
false,
93+
)
94+
7995
init {
8096
viewModelScope.launch {
8197
configTriggerUseCase.keyMap
@@ -84,9 +100,17 @@ class OnboardingTipDelegateImpl @Inject constructor(
84100
onCollectTrigger(trigger)
85101
}
86102
}
103+
104+
viewModelScope.launch {
105+
configActionsUseCase.keyMap
106+
.mapNotNull { it.dataOrNull()?.actionList }
107+
.collect { actionList ->
108+
onCollectActions(actionList)
109+
}
110+
}
87111
}
88112

89-
override fun onDismissClick() {
113+
override fun onTriggerTipDismissClick() {
90114
val currentTip = triggerTip.value
91115

92116
when (currentTip?.id) {
@@ -120,7 +144,29 @@ class OnboardingTipDelegateImpl @Inject constructor(
120144
triggerTip.value = null
121145
}
122146

123-
private suspend fun onCollectTrigger(trigger: Trigger) {
147+
override fun onActionTipDismissClick() {
148+
val currentTip = actionsTip.value
149+
150+
when (currentTip?.id) {
151+
RINGER_MODE_TIP_ID -> {
152+
shownRingerModeTip = true
153+
}
154+
}
155+
156+
actionsTip.value = null
157+
}
158+
159+
override fun onTipButtonClick(tipId: String) {
160+
when (tipId) {
161+
RINGER_MODE_TIP_ID -> {
162+
viewModelScope.launch {
163+
navigate("ringer_mode_tip_pro_mode", NavDestination.ProMode)
164+
}
165+
}
166+
}
167+
}
168+
169+
private fun onCollectTrigger(trigger: Trigger) {
124170
val showPowerButtonEmergencyTip = trigger.keys.any {
125171
it is KeyCodeTriggerKey && KeyEventUtils.isPowerButtonKey(
126172
it.keyCode,
@@ -214,10 +260,36 @@ class OnboardingTipDelegateImpl @Inject constructor(
214260
}
215261
}
216262
}
263+
264+
private fun onCollectActions(actionList: List<Action>) {
265+
val hasRingerModeAction = actionList.any { action ->
266+
when (action.data) {
267+
is ActionData.Volume.SetRingerMode,
268+
is ActionData.Volume.CycleRingerMode,
269+
is ActionData.Volume.CycleVibrateRing -> true
270+
271+
else -> false
272+
}
273+
}
274+
275+
if (hasRingerModeAction && !shownRingerModeTip && Build.VERSION.SDK_INT >= Constants.SYSTEM_BRIDGE_MIN_API) {
276+
val tip = OnboardingTipModel(
277+
id = RINGER_MODE_TIP_ID,
278+
title = getString(R.string.tip_ringer_mode_title),
279+
message = getString(R.string.tip_ringer_mode_text),
280+
isDismissable = true,
281+
buttonText = getString(R.string.tip_ringer_mode_button),
282+
)
283+
actionsTip.value = tip
284+
}
285+
}
217286
}
218287

219288
interface OnboardingTipDelegate {
220289
val triggerTip: StateFlow<OnboardingTipModel?>
290+
val actionsTip: StateFlow<OnboardingTipModel?>
221291

222-
fun onDismissClick()
292+
fun onTriggerTipDismissClick()
293+
fun onActionTipDismissClick()
294+
fun onTipButtonClick(tipId: String)
223295
}

base/src/main/java/io/github/sds100/keymapper/base/onboarding/OnboardingTipModel.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,5 @@ data class OnboardingTipModel(
55
val title: String,
66
val message: String,
77
val isDismissable: Boolean,
8+
val buttonText: String? = null,
89
)

base/src/main/java/io/github/sds100/keymapper/base/onboarding/TipCard.kt

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@ import androidx.compose.foundation.layout.width
1212
import androidx.compose.material.icons.Icons
1313
import androidx.compose.material.icons.rounded.Close
1414
import androidx.compose.material.icons.rounded.Info
15+
import androidx.compose.material3.ButtonDefaults
1516
import androidx.compose.material3.CardDefaults
1617
import androidx.compose.material3.Icon
1718
import androidx.compose.material3.IconButton
1819
import androidx.compose.material3.MaterialTheme
1920
import androidx.compose.material3.OutlinedCard
2021
import androidx.compose.material3.Text
22+
import androidx.compose.material3.TextButton
2123
import androidx.compose.runtime.Composable
2224
import androidx.compose.ui.Alignment
2325
import androidx.compose.ui.Modifier
@@ -30,8 +32,10 @@ fun TipCard(
3032
modifier: Modifier = Modifier,
3133
title: String,
3234
message: String,
35+
buttonText: String? = null,
3336
isDismissable: Boolean = true,
3437
onDismiss: () -> Unit = {},
38+
onButtonClick: () -> Unit = {},
3539
) {
3640
OutlinedCard(
3741
modifier = modifier,
@@ -70,7 +74,21 @@ fun TipCard(
7074
style = MaterialTheme.typography.bodyMedium,
7175
)
7276

73-
Spacer(modifier = Modifier.height(16.dp))
77+
if (buttonText != null) {
78+
Spacer(modifier = Modifier.height(8.dp))
79+
80+
TextButton(
81+
modifier = Modifier
82+
.padding(horizontal = 16.dp)
83+
.align(Alignment.End),
84+
onClick = onButtonClick,
85+
colors = ButtonDefaults.textButtonColors(contentColor = MaterialTheme.colorScheme.tertiary)
86+
) {
87+
Text(buttonText)
88+
}
89+
}
90+
91+
Spacer(modifier = Modifier.height(8.dp))
7492
}
7593

7694
if (isDismissable) {
@@ -86,8 +104,8 @@ fun TipCard(
86104
tint = MaterialTheme.colorScheme.onSurfaceVariant,
87105
)
88106
}
107+
}
89108
}
90-
}
91109
}
92110
}
93111

@@ -98,6 +116,7 @@ private fun TipCardPreview() {
98116
TipCard(
99117
title = "Tip Title",
100118
message = "This is a helpful tip message that explains something important to the user. It can be multiple lines long and provides useful information.",
119+
buttonText = "Button"
101120
)
102121
}
103122
}

base/src/main/java/io/github/sds100/keymapper/base/trigger/BaseTriggerScreen.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -118,7 +118,9 @@ fun BaseTriggerScreen(
118118
title = tip.title,
119119
message = tip.message,
120120
isDismissable = tip.isDismissable,
121-
onDismiss = viewModel::onDismissClick,
121+
onDismiss = viewModel::onTriggerTipDismissClick,
122+
buttonText = tip.buttonText,
123+
onButtonClick = { viewModel.onTipButtonClick(tip.id) },
122124
)
123125

124126
Spacer(Modifier.height(8.dp))

base/src/main/res/values/strings.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1362,6 +1362,9 @@
13621362
<string name="tip_screen_pinning_text">Using the back button as a trigger may conflict with app pinning, causing a black screen on unlock. A reboot will fix it.</string>
13631363
<string name="tip_ime_detection_title">Keyboard icon</string>
13641364
<string name="tip_ime_detection_text">The ⌨ symbol means you must use the Key Mapper input method for this trigger to work due to an Android restriction.</string>
1365+
<string name="tip_ringer_mode_title">Ringer mode actions</string>
1366+
<string name="tip_ringer_mode_text">Consider using PRO mode for ringer mode actions to avoid conflicts with Do Not Disturb settings.</string>
1367+
<string name="tip_ringer_mode_button">Use PRO mode</string>
13651368
<string name="trigger_constraints_tip_title">Limit to specific apps?</string>
13661369
<string name="trigger_constraints_tip_text">Add constraints in the Constraints tab.</string>
13671370
<string name="choose_floating_layout_title">Choose a layout</string>

data/src/main/java/io/github/sds100/keymapper/data/Keys.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ object Keys {
5959
booleanPreferencesKey("key_shown_caps_lock_tip")
6060
val shownScreenPinningTip =
6161
booleanPreferencesKey("key_shown_screen_pinning_tip")
62+
val shownRingerModeTip =
63+
booleanPreferencesKey("key_shown_ringer_mode_tip")
6264
val lastInstalledVersionCodeHomeScreen =
6365
intPreferencesKey("last_installed_version_home_screen")
6466
val lastInstalledVersionCodeBackground =

sysbridge/src/main/aidl/io/github/sds100/keymapper/sysbridge/ISystemBridge.aidl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,4 +40,6 @@ interface ISystemBridge {
4040
void forceStopPackage(String packageName) = 16;
4141

4242
void removeTasks(String packageName) = 17;
43+
44+
void setRingerMode(int ringerMode) = 18;
4345
}

0 commit comments

Comments
 (0)