Skip to content

Commit e6ec107

Browse files
committed
1.0.10
+ Customize controls Took 2 hours 35 minutes
1 parent 9267465 commit e6ec107

19 files changed

+1340
-812
lines changed

app/build.gradle.kts

+2-2
Original file line numberDiff line numberDiff line change
@@ -23,8 +23,8 @@ android {
2323
applicationId = "fr.angel.soundtap"
2424
minSdk = 30
2525
targetSdk = 34
26-
versionCode = 29
27-
versionName = "1.0.9"
26+
versionCode = 32
27+
versionName = "1.1.0"
2828

2929
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
3030
vectorDrawables {

app/src/main/java/fr/angel/soundtap/MainActivity.kt

+20-13
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
/*
2-
* Copyright 2024 Angel Studio
32
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
3+
* * Copyright (c) 2024 Angel Studio
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * http://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
716
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
1517
*/
1618
package fr.angel.soundtap
1719

@@ -108,6 +110,7 @@ class MainActivity : ComponentActivity() {
108110
}
109111

110112
LaunchedEffect(key1 = Unit) {
113+
mainViewModel.setDefaultNavController(navController)
111114
mainViewModel.updatePermissionStates(this@MainActivity)
112115
}
113116

@@ -144,7 +147,9 @@ class MainActivity : ComponentActivity() {
144147
enter = scaleIn(),
145148
exit = scaleOut(),
146149
) {
147-
IconButton(onClick = { navController.popBackStack() }) {
150+
IconButton(onClick = {
151+
uiState.currentNavController.navigateUp()
152+
}) {
148153
Icon(
149154
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
150155
contentDescription = "Back",
@@ -211,7 +216,9 @@ class MainActivity : ComponentActivity() {
211216
)
212217
}
213218
Spacer(modifier = Modifier.height(24.dp))
214-
sheetState.content(sheetState)
219+
sheetState.content(sheetState) {
220+
scope.launch { mainViewModel.hideBottomSheet() }
221+
}
215222
Spacer(modifier = Modifier.navigationBarsPadding())
216223
}
217224
}

app/src/main/java/fr/angel/soundtap/MainViewModel.kt

+66-11
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,19 @@
11
/*
2-
* Copyright 2024 Angel Studio
32
*
4-
* Licensed under the Apache License, Version 2.0 (the "License");
5-
* you may not use this file except in compliance with the License.
6-
* You may obtain a copy of the License at
3+
* * Copyright (c) 2024 Angel Studio
4+
* *
5+
* * Licensed under the Apache License, Version 2.0 (the "License");
6+
* * you may not use this file except in compliance with the License.
7+
* * You may obtain a copy of the License at
8+
* *
9+
* * http://www.apache.org/licenses/LICENSE-2.0
10+
* *
11+
* * Unless required by applicable law or agreed to in writing, software
12+
* * distributed under the License is distributed on an "AS IS" BASIS,
13+
* * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* * See the License for the specific language governing permissions and
15+
* * limitations under the License.
716
*
8-
* http://www.apache.org/licenses/LICENSE-2.0
9-
*
10-
* Unless required by applicable law or agreed to in writing, software
11-
* distributed under the License is distributed on an "AS IS" BASIS,
12-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13-
* See the License for the specific language governing permissions and
14-
* limitations under the License.
1517
*/
1618
package fr.angel.soundtap
1719

@@ -23,12 +25,14 @@ import androidx.compose.material3.SheetState
2325
import androidx.datastore.core.DataStore
2426
import androidx.lifecycle.ViewModel
2527
import androidx.lifecycle.viewModelScope
28+
import androidx.navigation.NavHostController
2629
import dagger.hilt.android.lifecycle.HiltViewModel
2730
import dagger.hilt.android.qualifiers.ApplicationContext
2831
import fr.angel.soundtap.data.enums.AutoPlayMode
2932
import fr.angel.soundtap.data.enums.HapticFeedbackLevel
3033
import fr.angel.soundtap.data.enums.WorkingMode
3134
import fr.angel.soundtap.data.models.BottomSheetState
35+
import fr.angel.soundtap.data.settings.customization.ControlMediaAction
3236
import fr.angel.soundtap.data.settings.customization.CustomizationSettings
3337
import fr.angel.soundtap.data.settings.settings.AppSettings
3438
import fr.angel.soundtap.data.settings.stats.StatsSettings
@@ -52,7 +56,11 @@ data class MainUiState(
5256
val customizationSettings: CustomizationSettings = CustomizationSettings(),
5357
val appSettings: AppSettings = AppSettings(),
5458
val statsSettings: StatsSettings = StatsSettings(),
59+
val defaultNavController: NavHostController? = null,
60+
val focusedNavController: NavHostController? = null,
5561
) {
62+
val currentNavController: NavHostController
63+
get() = focusedNavController ?: defaultNavController!!
5664
val defaultScreen: Screens
5765
get() = if (appSettings.onboardingPageCompleted) Screens.App else Screens.Onboarding
5866
}
@@ -208,4 +216,51 @@ class MainViewModel
208216
fun setBottomSheetState(sheetState: SheetState) {
209217
this.sheetState = sheetState
210218
}
219+
220+
fun setDefaultNavController(navController: NavHostController) {
221+
_uiState.value = _uiState.value.copy(defaultNavController = navController)
222+
}
223+
224+
fun setFocusedNavController(navController: NavHostController) {
225+
_uiState.value = _uiState.value.copy(focusedNavController = navController)
226+
}
227+
228+
fun resetFocusedNavController() {
229+
_uiState.value = _uiState.value.copy(focusedNavController = null)
230+
}
231+
232+
fun toggleControlMediaAction(action: ControlMediaAction) {
233+
viewModelScope.launch {
234+
customizationSettingsDataStore.updateData { settings ->
235+
val newAction = action.copy(enabled = !action.enabled)
236+
when (action.id) {
237+
0 -> settings.copy(longVolumeUpPressControlMediaAction = newAction)
238+
1 -> settings.copy(longVolumeDownPressControlMediaAction = newAction)
239+
2 -> settings.copy(doubleVolumeLongPressControlMediaAction = newAction)
240+
else -> settings
241+
}
242+
}
243+
}
244+
}
245+
246+
fun changeControlMediaAction(controlMediaAction: ControlMediaAction) {
247+
showBottomSheet(
248+
BottomSheetState.EditControlMediaAction(
249+
displayName = "Edit ${controlMediaAction.title} action",
250+
onSetAction = { newAction ->
251+
viewModelScope.launch {
252+
customizationSettingsDataStore.updateData { settings ->
253+
val newControlMediaAction = controlMediaAction.copy(action = newAction)
254+
when (controlMediaAction.id) {
255+
0 -> settings.copy(longVolumeUpPressControlMediaAction = newControlMediaAction)
256+
1 -> settings.copy(longVolumeDownPressControlMediaAction = newControlMediaAction)
257+
2 -> settings.copy(doubleVolumeLongPressControlMediaAction = newControlMediaAction)
258+
else -> settings
259+
}
260+
}
261+
}
262+
},
263+
),
264+
)
265+
}
211266
}

app/src/main/java/fr/angel/soundtap/data/models/BottomSheetState.kt

+59-5
Original file line numberDiff line numberDiff line change
@@ -18,26 +18,33 @@
1818

1919
package fr.angel.soundtap.data.models
2020

21+
import androidx.compose.foundation.layout.Arrangement
2122
import androidx.compose.foundation.layout.Column
23+
import androidx.compose.foundation.layout.Spacer
2224
import androidx.compose.foundation.layout.fillMaxWidth
2325
import androidx.compose.foundation.layout.padding
26+
import androidx.compose.foundation.layout.width
2427
import androidx.compose.material3.Button
2528
import androidx.compose.material3.ButtonDefaults
29+
import androidx.compose.material3.Icon
2630
import androidx.compose.material3.MaterialTheme
2731
import androidx.compose.material3.Text
2832
import androidx.compose.runtime.Composable
2933
import androidx.compose.ui.Modifier
3034
import androidx.compose.ui.text.style.TextAlign
3135
import androidx.compose.ui.unit.dp
36+
import fr.angel.soundtap.data.settings.customization.MediaAction
3237

3338
sealed class BottomSheetState(
3439
open val displayName: String?,
3540
open val onDismiss: (() -> Unit)? = null,
36-
val content: @Composable (BottomSheetState) -> Unit,
41+
val content: @Composable (BottomSheetState, () -> Unit) -> Unit,
3742
) {
3843
data object None : BottomSheetState(
3944
displayName = "Oops, something went wrong",
40-
content = { },
45+
content = { _, _ ->
46+
Text("An error occurred while trying to display the bottom sheet.")
47+
},
4148
)
4249

4350
data class SetTimer(
@@ -46,7 +53,7 @@ sealed class BottomSheetState(
4653
val onTimerSet: (Long) -> Unit,
4754
) : BottomSheetState(
4855
displayName = displayName,
49-
content = { state ->
56+
content = { state, hide ->
5057
val timerChoices: Map<String, Long> =
5158
mapOf(
5259
"5 minutes" to 5 * 60 * 1000,
@@ -58,7 +65,7 @@ sealed class BottomSheetState(
5865
)
5966

6067
Column(
61-
verticalArrangement = androidx.compose.foundation.layout.Arrangement.spacedBy(4.dp),
68+
verticalArrangement = Arrangement.spacedBy(4.dp),
6269
) {
6370
timerChoices.forEach { (label, duration) ->
6471
Button(
@@ -71,17 +78,64 @@ sealed class BottomSheetState(
7178
),
7279
onClick = {
7380
onTimerSet(duration)
81+
hide()
82+
state.onDismiss?.invoke()
83+
},
84+
) {
85+
Text(
86+
modifier =
87+
Modifier
88+
.fillMaxWidth()
89+
.padding(vertical = 8.dp),
90+
text = label,
91+
textAlign = TextAlign.Start,
92+
)
93+
}
94+
}
95+
}
96+
},
97+
)
98+
99+
data class EditControlMediaAction(
100+
override val displayName: String? = "Control Media Action",
101+
override val onDismiss: (() -> Unit)? = null,
102+
val onSetAction: (MediaAction) -> Unit,
103+
) : BottomSheetState(
104+
displayName = displayName,
105+
content = { state, hide ->
106+
val options = MediaAction.entries
74107

108+
Column(
109+
verticalArrangement = Arrangement.spacedBy(4.dp),
110+
) {
111+
options.forEach { action ->
112+
Button(
113+
modifier = Modifier.fillMaxWidth(),
114+
shape = MaterialTheme.shapes.medium,
115+
colors =
116+
ButtonDefaults.buttonColors(
117+
containerColor = MaterialTheme.colorScheme.surfaceContainerHigh,
118+
contentColor = MaterialTheme.colorScheme.onSurface,
119+
),
120+
onClick = {
121+
onSetAction(action)
122+
123+
hide()
75124
// Dismiss the bottom sheet
76125
state.onDismiss?.invoke()
77126
},
78127
) {
128+
Icon(
129+
imageVector = action.icon,
130+
contentDescription = null,
131+
)
132+
Spacer(modifier = Modifier.width(8.dp))
79133
Text(
80134
modifier =
81135
Modifier
82136
.fillMaxWidth()
83137
.padding(vertical = 8.dp),
84-
text = label,
138+
text = action.title,
85139
textAlign = TextAlign.Start,
86140
)
87141
}

0 commit comments

Comments
 (0)