Skip to content

Commit c32862e

Browse files
committed
Merge branch 'feature/699-time-constraints' into develop
2 parents 5d95314 + 43bace5 commit c32862e

16 files changed

Lines changed: 459 additions & 0 deletions

File tree

CHANGELOG.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
1+
## [3.1.0](https://github.com/sds100/KeyMapper/releases/tag/v3.1.0)
2+
3+
#### TO BE RELEASED
4+
5+
## Added
6+
7+
- #699 Time constraints ⏰
8+
19
## [3.0.1](https://github.com/sds100/KeyMapper/releases/tag/v3.0.1)
210

311
#### 28 April 2025

app/build.gradle

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,9 @@ android {
119119
}
120120

121121
compileOptions {
122+
// Required for desugaring new Java time API on lower than API 26
123+
coreLibraryDesugaringEnabled = true
124+
122125
sourceCompatibility JavaVersion.VERSION_17
123126
targetCompatibility JavaVersion.VERSION_17
124127
}
@@ -192,6 +195,7 @@ dependencies {
192195
proImplementation 'com.revenuecat.purchases:purchases:8.15.0'
193196
proImplementation "com.airbnb.android:lottie-compose:6.6.3"
194197
implementation("com.squareup.okhttp3:okhttp:4.12.0")
198+
coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5")
195199

196200
// splitties
197201
implementation "com.louiscad.splitties:splitties-bitflags:$splitties_version"

app/src/main/java/io/github/sds100/keymapper/actions/FlashlightActionBottomSheet.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ import androidx.compose.material3.SheetValue
3535
import androidx.compose.material3.Slider
3636
import androidx.compose.material3.Text
3737
import androidx.compose.material3.TextButton
38+
import androidx.compose.material3.TimePicker
3839
import androidx.compose.material3.rememberModalBottomSheetState
40+
import androidx.compose.material3.rememberTimePickerState
3941
import androidx.compose.runtime.Composable
4042
import androidx.compose.runtime.remember
4143
import androidx.compose.runtime.rememberCoroutineScope

app/src/main/java/io/github/sds100/keymapper/constraints/ChooseConstraintScreen.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ fun ChooseConstraintScreen(
6262
val listItems by viewModel.listItems.collectAsStateWithLifecycle()
6363
val query by viewModel.searchQuery.collectAsStateWithLifecycle()
6464

65+
TimeConstraintBottomSheet(viewModel)
66+
6567
ChooseConstraintScreen(
6668
modifier = modifier,
6769
state = listItems,

app/src/main/java/io/github/sds100/keymapper/constraints/ChooseConstraintViewModel.kt

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
package io.github.sds100.keymapper.constraints
22

3+
import androidx.compose.runtime.getValue
4+
import androidx.compose.runtime.mutableStateOf
5+
import androidx.compose.runtime.setValue
36
import androidx.lifecycle.ViewModel
47
import androidx.lifecycle.ViewModelProvider
58
import androidx.lifecycle.viewModelScope
@@ -88,6 +91,8 @@ class ChooseConstraintViewModel(
8891

8992
ConstraintId.CHARGING,
9093
ConstraintId.DISCHARGING,
94+
95+
ConstraintId.TIME,
9196
)
9297
}
9398

@@ -104,6 +109,17 @@ class ChooseConstraintViewModel(
104109
State.Data(filteredItems)
105110
}.flowOn(Dispatchers.Default).stateIn(viewModelScope, SharingStarted.Eagerly, State.Loading)
106111

112+
var timeConstraintState: Constraint.Time? by mutableStateOf(null)
113+
114+
fun onDoneConfigTimeConstraintClick() {
115+
timeConstraintState?.let { constraint ->
116+
viewModelScope.launch {
117+
_returnResult.emit(constraint)
118+
timeConstraintState = null
119+
}
120+
}
121+
}
122+
107123
fun onListItemClick(id: String) {
108124
viewModelScope.launch {
109125
when (val constraintType = ConstraintId.valueOf(id)) {
@@ -192,6 +208,15 @@ class ChooseConstraintViewModel(
192208

193209
ConstraintId.LOCK_SCREEN_NOT_SHOWING ->
194210
_returnResult.emit(Constraint.LockScreenNotShowing())
211+
212+
ConstraintId.TIME -> {
213+
timeConstraintState = Constraint.Time(
214+
startHour = 0,
215+
startMinute = 0,
216+
endHour = 0,
217+
endMinute = 0,
218+
)
219+
}
195220
}
196221
}
197222
}

app/src/main/java/io/github/sds100/keymapper/constraints/Constraint.kt

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ import io.github.sds100.keymapper.system.display.Orientation
88
import io.github.sds100.keymapper.util.getKey
99
import io.github.sds100.keymapper.util.valueOrNull
1010
import kotlinx.serialization.Serializable
11+
import java.time.LocalTime
1112
import java.util.UUID
1213

1314
/**
@@ -216,6 +217,20 @@ sealed class Constraint {
216217
data class Discharging(override val uid: String = UUID.randomUUID().toString()) : Constraint() {
217218
override val id: ConstraintId = ConstraintId.DISCHARGING
218219
}
220+
221+
@Serializable
222+
data class Time(
223+
override val uid: String = UUID.randomUUID().toString(),
224+
val startHour: Int,
225+
val startMinute: Int,
226+
val endHour: Int,
227+
val endMinute: Int,
228+
) : Constraint() {
229+
override val id: ConstraintId = ConstraintId.TIME
230+
231+
val startTime: LocalTime by lazy { LocalTime.of(startHour, startMinute) }
232+
val endTime: LocalTime by lazy { LocalTime.of(endHour, endMinute) }
233+
}
219234
}
220235

221236
object ConstraintModeEntityMapper {
@@ -375,6 +390,28 @@ object ConstraintEntityMapper {
375390
ConstraintEntity.CHARGING -> Constraint.Charging(uid = entity.uid)
376391
ConstraintEntity.DISCHARGING -> Constraint.Discharging(uid = entity.uid)
377392

393+
ConstraintEntity.TIME -> {
394+
val startTime =
395+
entity.extras.getData(ConstraintEntity.EXTRA_START_TIME).valueOrNull()!!
396+
.split(":")
397+
val startHour = startTime[0].toInt()
398+
val startMin = startTime[1].toInt()
399+
400+
val endTime =
401+
entity.extras.getData(ConstraintEntity.EXTRA_END_TIME).valueOrNull()!!
402+
.split(":")
403+
val endHour = endTime[0].toInt()
404+
val endMin = endTime[1].toInt()
405+
406+
Constraint.Time(
407+
uid = entity.uid,
408+
startHour = startHour,
409+
startMinute = startMin,
410+
endHour = endHour,
411+
endMinute = endMin,
412+
)
413+
}
414+
378415
else -> throw Exception("don't know how to convert constraint entity with type ${entity.type}")
379416
}
380417
}
@@ -606,5 +643,18 @@ object ConstraintEntityMapper {
606643
uid = constraint.uid,
607644
ConstraintEntity.DISCHARGING,
608645
)
646+
647+
is Constraint.Time -> ConstraintEntity(
648+
uid = constraint.uid,
649+
type = ConstraintEntity.TIME,
650+
EntityExtra(
651+
ConstraintEntity.EXTRA_START_TIME,
652+
"${constraint.startHour}:${constraint.startMinute}",
653+
),
654+
EntityExtra(
655+
ConstraintEntity.EXTRA_END_TIME,
656+
"${constraint.endHour}:${constraint.endMinute}",
657+
),
658+
)
609659
}
610660
}

app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintId.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,4 +48,6 @@ enum class ConstraintId {
4848

4949
CHARGING,
5050
DISCHARGING,
51+
52+
TIME
5153
}

app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintSnapshot.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ import io.github.sds100.keymapper.system.phone.PhoneAdapter
1717
import io.github.sds100.keymapper.system.power.PowerAdapter
1818
import io.github.sds100.keymapper.util.firstBlocking
1919
import timber.log.Timber
20+
import java.time.LocalTime
2021

2122
/**
2223
* Created by sds100 on 08/05/2021.f
@@ -69,6 +70,8 @@ class LazyConstraintSnapshot(
6970
lockScreenAdapter.isLockScreenShowing()
7071
}
7172

73+
private val localTime = LocalTime.now()
74+
7275
private fun isMediaPlaying(): Boolean {
7376
return audioVolumeStreams.contains(AudioManager.STREAM_MUSIC) || appsPlayingMedia.isNotEmpty()
7477
}
@@ -156,6 +159,13 @@ class LazyConstraintSnapshot(
156159
// an another activity like the camera app while the phone is locked.
157160
is Constraint.LockScreenShowing -> isLockscreenShowing && appInForeground == "com.android.systemui"
158161
is Constraint.LockScreenNotShowing -> !isLockscreenShowing || appInForeground != "com.android.systemui"
162+
163+
is Constraint.Time ->
164+
if (constraint.startTime.isAfter(constraint.endTime)) {
165+
localTime.isAfter(constraint.startTime) || localTime.isBefore(constraint.endTime)
166+
} else {
167+
localTime.isAfter(constraint.startTime) && localTime.isBefore(constraint.endTime)
168+
}
159169
}
160170

161171
if (isSatisfied) {

app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintUiHelper.kt

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,12 @@ import androidx.compose.material.icons.rounded.Android
55
import io.github.sds100.keymapper.R
66
import io.github.sds100.keymapper.system.camera.CameraLens
77
import io.github.sds100.keymapper.system.display.Orientation
8+
import io.github.sds100.keymapper.util.TimeUtils
89
import io.github.sds100.keymapper.util.handle
910
import io.github.sds100.keymapper.util.ui.ResourceProvider
1011
import io.github.sds100.keymapper.util.ui.compose.ComposeIconInfo
1112
import io.github.sds100.keymapper.util.valueIfFailure
13+
import java.time.format.FormatStyle
1214

1315
/**
1416
* Created by sds100 on 18/03/2021.
@@ -20,6 +22,8 @@ class ConstraintUiHelper(
2022
) : DisplayConstraintUseCase by displayConstraintUseCase,
2123
ResourceProvider by resourceProvider {
2224

25+
private val timeFormatter by lazy { TimeUtils.localeDateFormatter(FormatStyle.SHORT) }
26+
2327
fun getTitle(constraint: Constraint): String = when (constraint) {
2428
is Constraint.AppInForeground ->
2529
getAppName(constraint.packageName).handle(
@@ -144,6 +148,13 @@ class ConstraintUiHelper(
144148
is Constraint.Discharging -> getString(R.string.constraint_discharging)
145149
is Constraint.LockScreenShowing -> getString(R.string.constraint_lock_screen_showing)
146150
is Constraint.LockScreenNotShowing -> getString(R.string.constraint_lock_screen_not_showing)
151+
is Constraint.Time -> getString(
152+
R.string.constraint_time_formatted,
153+
arrayOf(
154+
timeFormatter.format(constraint.startTime),
155+
timeFormatter.format(constraint.endTime),
156+
),
157+
)
147158
}
148159

149160
fun getIcon(constraint: Constraint): ComposeIconInfo = when (constraint) {

app/src/main/java/io/github/sds100/keymapper/constraints/ConstraintUtils.kt

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import androidx.compose.material.icons.outlined.SignalWifiStatusbarNull
2020
import androidx.compose.material.icons.outlined.StayCurrentLandscape
2121
import androidx.compose.material.icons.outlined.StayCurrentPortrait
2222
import androidx.compose.material.icons.outlined.StopCircle
23+
import androidx.compose.material.icons.outlined.Timer
2324
import androidx.compose.material.icons.outlined.Wifi
2425
import androidx.compose.material.icons.outlined.WifiOff
2526
import androidx.compose.material.icons.rounded.Android
@@ -78,6 +79,7 @@ object ConstraintUtils {
7879
ConstraintId.DISCHARGING -> ComposeIconInfo.Vector(Icons.Outlined.Battery2Bar)
7980
ConstraintId.LOCK_SCREEN_SHOWING -> ComposeIconInfo.Vector(Icons.Outlined.ScreenLockPortrait)
8081
ConstraintId.LOCK_SCREEN_NOT_SHOWING -> ComposeIconInfo.Vector(Icons.Outlined.LockOpen)
82+
ConstraintId.TIME -> ComposeIconInfo.Vector(Icons.Outlined.Timer)
8183
}
8284

8385
fun getTitleStringId(constraintId: ConstraintId): Int = when (constraintId) {
@@ -114,5 +116,6 @@ object ConstraintUtils {
114116
ConstraintId.DISCHARGING -> R.string.constraint_discharging
115117
ConstraintId.LOCK_SCREEN_SHOWING -> R.string.constraint_lock_screen_showing
116118
ConstraintId.LOCK_SCREEN_NOT_SHOWING -> R.string.constraint_lock_screen_not_showing
119+
ConstraintId.TIME -> R.string.constraint_time
117120
}
118121
}

0 commit comments

Comments
 (0)