feat: Add vibration when timer completes, reduce loop frequency when app is in background
This commit is contained in:
@@ -6,6 +6,7 @@
|
|||||||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_SPECIAL_USE" />
|
||||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||||
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS" />
|
<uses-permission android:name="android.permission.POST_PROMOTED_NOTIFICATIONS" />
|
||||||
|
<uses-permission android:name="android.permission.VIBRATE" />
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:name=".TomatoApplication"
|
android:name=".TomatoApplication"
|
||||||
|
|||||||
@@ -18,6 +18,10 @@ class MainActivity : ComponentActivity() {
|
|||||||
private val timerViewModel: TimerViewModel by viewModels(factoryProducer = { TimerViewModel.Factory })
|
private val timerViewModel: TimerViewModel by viewModels(factoryProducer = { TimerViewModel.Factory })
|
||||||
private val statsViewModel: StatsViewModel by viewModels(factoryProducer = { StatsViewModel.Factory })
|
private val statsViewModel: StatsViewModel by viewModels(factoryProducer = { StatsViewModel.Factory })
|
||||||
|
|
||||||
|
private val appContainer by lazy {
|
||||||
|
(application as TomatoApplication).container
|
||||||
|
}
|
||||||
|
|
||||||
override fun onCreate(savedInstanceState: Bundle?) {
|
override fun onCreate(savedInstanceState: Bundle?) {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
enableEdgeToEdge()
|
enableEdgeToEdge()
|
||||||
@@ -29,6 +33,18 @@ class MainActivity : ComponentActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun onStop() {
|
||||||
|
super.onStop()
|
||||||
|
// Reduce the timer loop frequency when not visible to save battery power
|
||||||
|
appContainer.appTimerRepository.timerFrequency = 1f
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStart() {
|
||||||
|
super.onStart()
|
||||||
|
// Increase the timer loop frequency again when visible to make the progress smoother
|
||||||
|
appContainer.appTimerRepository.timerFrequency = 10f
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val screens = listOf(
|
val screens = listOf(
|
||||||
NavItem(
|
NavItem(
|
||||||
|
|||||||
@@ -16,6 +16,7 @@ interface TimerRepository {
|
|||||||
var shortBreakTime: Long
|
var shortBreakTime: Long
|
||||||
var longBreakTime: Long
|
var longBreakTime: Long
|
||||||
var sessionLength: Int
|
var sessionLength: Int
|
||||||
|
var timerFrequency: Float
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -26,4 +27,5 @@ class AppTimerRepository : TimerRepository {
|
|||||||
override var shortBreakTime = 5 * 60 * 1000L
|
override var shortBreakTime = 5 * 60 * 1000L
|
||||||
override var longBreakTime = 15 * 60 * 1000L
|
override var longBreakTime = 15 * 60 * 1000L
|
||||||
override var sessionLength = 4
|
override var sessionLength = 4
|
||||||
|
override var timerFrequency: Float = 10f
|
||||||
}
|
}
|
||||||
@@ -7,6 +7,9 @@ import android.media.MediaPlayer
|
|||||||
import android.os.Build
|
import android.os.Build
|
||||||
import android.os.IBinder
|
import android.os.IBinder
|
||||||
import android.os.SystemClock
|
import android.os.SystemClock
|
||||||
|
import android.os.VibrationEffect
|
||||||
|
import android.os.Vibrator
|
||||||
|
import android.os.VibratorManager
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import androidx.compose.material3.ColorScheme
|
import androidx.compose.material3.ColorScheme
|
||||||
import androidx.compose.material3.lightColorScheme
|
import androidx.compose.material3.lightColorScheme
|
||||||
@@ -62,7 +65,23 @@ class TimerService : Service() {
|
|||||||
private val scope = CoroutineScope(Dispatchers.IO + job)
|
private val scope = CoroutineScope(Dispatchers.IO + job)
|
||||||
private val skipScope = CoroutineScope(Dispatchers.IO + job)
|
private val skipScope = CoroutineScope(Dispatchers.IO + job)
|
||||||
|
|
||||||
private lateinit var alarm: MediaPlayer
|
private val alarm by lazy {
|
||||||
|
MediaPlayer.create(
|
||||||
|
this,
|
||||||
|
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
private val vibrator by lazy {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
|
val vibratorManager =
|
||||||
|
getSystemService(VIBRATOR_MANAGER_SERVICE) as VibratorManager
|
||||||
|
vibratorManager.defaultVibrator
|
||||||
|
} else {
|
||||||
|
@Suppress("DEPRECATION")
|
||||||
|
getSystemService(VIBRATOR_SERVICE) as Vibrator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private var cs: ColorScheme = lightColorScheme()
|
private var cs: ColorScheme = lightColorScheme()
|
||||||
|
|
||||||
@@ -80,11 +99,6 @@ class TimerService : Service() {
|
|||||||
_time = appContainer.time
|
_time = appContainer.time
|
||||||
|
|
||||||
timerState = _timerState.asStateFlow()
|
timerState = _timerState.asStateFlow()
|
||||||
|
|
||||||
alarm = MediaPlayer.create(
|
|
||||||
this,
|
|
||||||
Settings.System.DEFAULT_ALARM_ALERT_URI ?: Settings.System.DEFAULT_RINGTONE_URI
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
@@ -147,7 +161,8 @@ class TimerService : Service() {
|
|||||||
else -> timerRepository.longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
else -> timerRepository.longBreakTime - (SystemClock.elapsedRealtime() - startTime - pauseDuration).toInt()
|
||||||
}
|
}
|
||||||
|
|
||||||
iterations = (iterations + 1) % 10
|
iterations =
|
||||||
|
(iterations + 1) % timerRepository.timerFrequency.toInt().coerceAtLeast(1)
|
||||||
|
|
||||||
if (iterations == 0) showTimerNotification(time.toInt())
|
if (iterations == 0) showTimerNotification(time.toInt())
|
||||||
|
|
||||||
@@ -165,7 +180,7 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
delay(100)
|
delay((1000f / timerRepository.timerFrequency).toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -254,7 +269,7 @@ class TimerService : Service() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
if (complete) {
|
if (complete) {
|
||||||
alarm.start()
|
startAlarm()
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(alarmRinging = true)
|
currentState.copy(alarmRinging = true)
|
||||||
}
|
}
|
||||||
@@ -328,9 +343,25 @@ class TimerService : Service() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun startAlarm() {
|
||||||
|
alarm.start()
|
||||||
|
|
||||||
|
if (!vibrator.hasVibrator()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val vibrationPattern = longArrayOf(0, 1000, 1000, 1000)
|
||||||
|
val repeat = 2
|
||||||
|
|
||||||
|
val effect = VibrationEffect.createWaveform(vibrationPattern, repeat)
|
||||||
|
vibrator.vibrate(effect)
|
||||||
|
}
|
||||||
|
|
||||||
fun stopAlarm() {
|
fun stopAlarm() {
|
||||||
alarm.pause()
|
alarm.pause()
|
||||||
alarm.seekTo(0)
|
alarm.seekTo(0)
|
||||||
|
vibrator.cancel()
|
||||||
|
|
||||||
_timerState.update { currentState ->
|
_timerState.update { currentState ->
|
||||||
currentState.copy(alarmRinging = false)
|
currentState.copy(alarmRinging = false)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user