diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 905e3c79..25522313 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -141,6 +141,14 @@ + + + + + binding.reminderDraggable.setOnTouchListener { _, event -> when (event.action) { MotionEvent.ACTION_DOWN -> { dragDownX = event.x @@ -188,16 +156,12 @@ class ReminderActivity : SimpleActivity() { didVibrate = true finishActivity() } - - cancelNotification() } else if (binding.reminderDraggable.x <= minDragX + 50f) { if (!didVibrate) { binding.reminderDraggable.performHapticFeedback() didVibrate = true snoozeAlarm() } - - cancelNotification() } } } @@ -224,61 +188,6 @@ class ReminderActivity : SimpleActivity() { } } - private fun setupEffects() { - audioManager = getSystemService(AUDIO_SERVICE) as AudioManager - initialAlarmVolume = audioManager?.getStreamVolume(AudioManager.STREAM_ALARM) ?: 7 - - val doVibrate = alarm?.vibrate ?: config.timerVibrate - if (doVibrate && isOreoPlus()) { - val pattern = LongArray(2) { 500 } - vibrationHandler.postDelayed({ - vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator - vibrator?.vibrate(VibrationEffect.createWaveform(pattern, 0)) - }, 500) - } - - val soundUri = if (alarm != null) { - alarm!!.soundUri - } else { - config.timerSoundUri - } - - if (soundUri != SILENT) { - try { - mediaPlayer = MediaPlayer().apply { - setAudioStreamType(AudioManager.STREAM_ALARM) - setDataSource(this@ReminderActivity, soundUri.toUri()) - isLooping = true - prepare() - start() - } - - if (config.increaseVolumeGradually) { - scheduleVolumeIncrease( - lastVolume = MIN_ALARM_VOLUME_FOR_INCREASING_ALARMS.toFloat(), - maxVolume = initialAlarmVolume!!.toFloat(), - delay = 0 - ) - } - } catch (e: Exception) { - } - } - } - - private fun scheduleVolumeIncrease(lastVolume: Float, maxVolume: Float, delay: Long) { - increaseVolumeHandler.postDelayed({ - val newLastVolume = (lastVolume + 0.1f).coerceAtMost(maxVolume) - audioManager?.setStreamVolume(AudioManager.STREAM_ALARM, newLastVolume.toInt(), 0) - scheduleVolumeIncrease(newLastVolume, maxVolume, INCREASE_VOLUME_DELAY) - }, delay) - } - - private fun resetVolumeToInitialValue() { - initialAlarmVolume?.apply { - audioManager?.setStreamVolume(AudioManager.STREAM_ALARM, this, 0) - } - } - override fun onConfigurationChanged(newConfig: Configuration) { super.onConfigurationChanged(newConfig) setupAlarmButtons() @@ -300,32 +209,11 @@ class ReminderActivity : SimpleActivity() { override fun onDestroy() { super.onDestroy() - increaseVolumeHandler.removeCallbacksAndMessages(null) - maxReminderDurationHandler.removeCallbacksAndMessages(null) swipeGuideFadeHandler.removeCallbacksAndMessages(null) - vibrationHandler.removeCallbacksAndMessages(null) - if (!finished) { - finishActivity() - cancelNotification() - } else { - destroyEffects() - } - } - - private fun destroyEffects() { - if (config.increaseVolumeGradually) { - resetVolumeToInitialValue() - } - - mediaPlayer?.stop() - mediaPlayer?.release() - mediaPlayer = null - vibrator?.cancel() - vibrator = null } private fun snoozeAlarm(overrideSnoozeDuration: Int? = null) { - destroyEffects() + stopAlarmService() if (overrideSnoozeDuration != null) { scheduleSnoozedAlarm(overrideSnoozeDuration) } else if (config.useSameSnooze) { @@ -346,18 +234,22 @@ class ReminderActivity : SimpleActivity() { } private fun scheduleSnoozedAlarm(snoozeMinutes: Int) { - setupAlarmClock( - alarm = alarm!!, - triggerTimeMillis = Calendar.getInstance() - .apply { add(Calendar.MINUTE, snoozeMinutes) } - .timeInMillis - ) + if (alarm != null) { + setupAlarmClock( + alarm = alarm!!, + triggerTimeMillis = Calendar.getInstance() + .apply { add(Calendar.MINUTE, snoozeMinutes) } + .timeInMillis + ) + + wasAlarmSnoozed = true + } - wasAlarmSnoozed = true finishActivity() } private fun finishActivity() { + stopAlarmService() if (!wasAlarmSnoozed && alarm != null) { cancelAlarmClock(alarm!!) if (alarm!!.days > 0) { @@ -367,8 +259,6 @@ class ReminderActivity : SimpleActivity() { disableExpiredAlarm(alarm!!) } - finished = true - destroyEffects() finish() overridePendingTransition(0, 0) } @@ -386,8 +276,4 @@ class ReminderActivity : SimpleActivity() { setTurnScreenOn(true) } } - - private fun cancelNotification() { - notificationManager.cancel(ALARM_NOTIF_ID) - } } diff --git a/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt b/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt index be807699..1b4d0dff 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/SnoozeReminderActivity.kt @@ -4,8 +4,8 @@ import android.os.Bundle import androidx.appcompat.app.AppCompatActivity import org.fossify.clock.extensions.config import org.fossify.clock.extensions.dbHelper -import org.fossify.clock.extensions.hideNotification import org.fossify.clock.extensions.setupAlarmClock +import org.fossify.clock.extensions.stopAlarmService import org.fossify.clock.helpers.ALARM_ID import org.fossify.commons.extensions.showPickSecondsDialog import org.fossify.commons.helpers.MINUTE_SECONDS @@ -16,7 +16,7 @@ class SnoozeReminderActivity : AppCompatActivity() { super.onCreate(savedInstanceState) val id = intent.getIntExtra(ALARM_ID, -1) val alarm = dbHelper.getAlarmWithId(id) ?: return - hideNotification(id) + stopAlarmService() showPickSecondsDialog( curSeconds = config.snoozeTime * MINUTE_SECONDS, isSnoozePicker = true, diff --git a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt index 5dc93be9..1746adae 100644 --- a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt @@ -14,7 +14,6 @@ import android.media.AudioManager.STREAM_ALARM import android.media.RingtoneManager import android.os.Handler import android.os.Looper -import android.os.PowerManager import android.text.SpannableString import android.text.style.RelativeSizeSpan import android.widget.Toast @@ -52,7 +51,6 @@ import org.fossify.clock.helpers.formatTime import org.fossify.clock.helpers.getAllTimeZones import org.fossify.clock.helpers.getCurrentDayMinutes import org.fossify.clock.helpers.getDefaultTimeZoneTitle -import org.fossify.clock.helpers.getPassedSeconds import org.fossify.clock.helpers.getTimeOfNextAlarm import org.fossify.clock.interfaces.TimerDao import org.fossify.clock.models.Alarm @@ -65,6 +63,7 @@ import org.fossify.clock.receivers.DismissAlarmReceiver import org.fossify.clock.receivers.EarlyAlarmDismissalReceiver import org.fossify.clock.receivers.HideAlarmReceiver import org.fossify.clock.receivers.HideTimerReceiver +import org.fossify.clock.services.AlarmService import org.fossify.clock.services.SnoozeService import org.fossify.commons.extensions.formatMinutesToTimeString import org.fossify.commons.extensions.formatSecondsToTimeString @@ -360,23 +359,6 @@ fun Context.rescheduleEnabledAlarms() { } } -fun Context.isScreenOn() = (getSystemService(Context.POWER_SERVICE) as PowerManager).isScreenOn - -fun Context.showAlarmNotification(alarm: Alarm) { - val pendingIntent = getOpenAlarmTabIntent() - val notification = getAlarmNotification(pendingIntent, alarm) - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - try { - notificationManager.notify(alarm.id, notification) - } catch (e: Exception) { - showErrorToast(e) - } - - if (alarm.days > 0) { - scheduleNextAlarm(alarm, false) - } -} - fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification { var soundUri = timer.soundUri if (soundUri == SILENT) { @@ -481,68 +463,6 @@ fun Context.getDismissAlarmPendingIntent(alarmId: Int, notificationId: Int): Pen return PendingIntent.getBroadcast(this, alarmId, intent, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) } -fun Context.getAlarmNotification(pendingIntent: PendingIntent, alarm: Alarm): Notification { - val soundUri = alarm.soundUri - if (soundUri != SILENT) { - grantReadUriPermission(soundUri) - } - val channelId = "simple_alarm_channel_${soundUri}_${alarm.vibrate}" - val label = alarm.label.ifEmpty { - getString(org.fossify.commons.R.string.alarm) - } - - if (isOreoPlus()) { - val audioAttributes = AudioAttributes.Builder() - .setUsage(AudioAttributes.USAGE_ALARM) - .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) - .setLegacyStreamType(STREAM_ALARM) - .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) - .build() - - val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - val importance = NotificationManager.IMPORTANCE_HIGH - NotificationChannel(channelId, label, importance).apply { - setBypassDnd(true) - enableLights(true) - lightColor = getProperPrimaryColor() - enableVibration(alarm.vibrate) - setSound(soundUri.toUri(), audioAttributes) - notificationManager.createNotificationChannel(this) - } - } - - val dismissIntent = getHideAlarmPendingIntent(alarm, channelId) - val builder = NotificationCompat.Builder(this) - .setContentTitle(label) - .setContentText(getFormattedTime(getPassedSeconds(), false, false)) - .setSmallIcon(R.drawable.ic_alarm_vector) - .setContentIntent(pendingIntent) - .setPriority(Notification.PRIORITY_HIGH) - .setDefaults(Notification.DEFAULT_LIGHTS) - .setChannelId(channelId) - .addAction( - org.fossify.commons.R.drawable.ic_snooze_vector, - getString(org.fossify.commons.R.string.snooze), - getSnoozePendingIntent(alarm) - ) - .addAction(org.fossify.commons.R.drawable.ic_cross_vector, getString(org.fossify.commons.R.string.dismiss), dismissIntent) - .setDeleteIntent(dismissIntent) - .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) - - if (soundUri != SILENT) { - builder.setSound(soundUri.toUri(), STREAM_ALARM) - } - - if (alarm.vibrate) { - val vibrateArray = LongArray(2) { 500 } - builder.setVibrate(vibrateArray) - } - - val notification = builder.build() - notification.flags = notification.flags or Notification.FLAG_INSISTENT - return notification -} - fun Context.getSnoozePendingIntent(alarm: Alarm): PendingIntent { val snoozeClass = if (config.useSameSnooze) SnoozeService::class.java else SnoozeReminderActivity::class.java val intent = Intent(this, snoozeClass).setAction("Snooze") @@ -613,4 +533,9 @@ fun Context.disableExpiredAlarm(alarm: Alarm) { updateWidgets() EventBus.getDefault().post(AlarmEvent.Refresh) } +} + +fun Context.stopAlarmService() { + val serviceIntent = Intent(this, AlarmService::class.java) + stopService(serviceIntent) } \ No newline at end of file diff --git a/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt b/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt index a0799fb9..32f7ec16 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt @@ -1,31 +1,12 @@ package org.fossify.clock.receivers -import android.app.NotificationChannel -import android.app.NotificationManager -import android.app.PendingIntent -import android.app.PendingIntent.FLAG_IMMUTABLE -import android.app.PendingIntent.FLAG_UPDATE_CURRENT import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import android.os.Build -import android.os.Handler -import android.os.Looper -import androidx.annotation.RequiresApi -import androidx.core.app.NotificationCompat -import org.fossify.clock.R -import org.fossify.clock.activities.ReminderActivity -import org.fossify.clock.extensions.config -import org.fossify.clock.extensions.dbHelper -import org.fossify.clock.extensions.disableExpiredAlarm import org.fossify.clock.extensions.hideNotification -import org.fossify.clock.extensions.isScreenOn -import org.fossify.clock.extensions.showAlarmNotification import org.fossify.clock.helpers.ALARM_ID -import org.fossify.clock.helpers.ALARM_NOTIFICATION_CHANNEL_ID -import org.fossify.clock.helpers.ALARM_NOTIF_ID import org.fossify.clock.helpers.EARLY_ALARM_NOTIF_ID -import org.fossify.commons.extensions.notificationManager +import org.fossify.clock.services.AlarmService import org.fossify.commons.extensions.showErrorToast import org.fossify.commons.helpers.isOreoPlus @@ -33,69 +14,22 @@ class AlarmReceiver : BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { val id = intent.getIntExtra(ALARM_ID, -1) - val alarm = context.dbHelper.getAlarmWithId(id) ?: return + if (id == -1) return // Hide early dismissal notification if not already dismissed context.hideNotification(EARLY_ALARM_NOTIF_ID) - if (context.isScreenOn()) { - context.showAlarmNotification(alarm) - Handler(Looper.getMainLooper()).postDelayed({ - context.hideNotification(id) - context.disableExpiredAlarm(alarm) - }, context.config.alarmMaxReminderSecs * 1000L) - } else { - if (isOreoPlus()) { - val notificationManager = context.notificationManager - if (notificationManager.getNotificationChannel(ALARM_NOTIFICATION_CHANNEL_ID) == null) { - // cleans up previous notification channel that had sound properties - oldNotificationChannelCleanup(notificationManager) - - NotificationChannel( - ALARM_NOTIFICATION_CHANNEL_ID, - "Alarm", - NotificationManager.IMPORTANCE_HIGH - ).apply { - setBypassDnd(true) - setSound(null, null) - notificationManager.createNotificationChannel(this) - } - } - - val reminderIntent = Intent(context, ReminderActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - putExtra(ALARM_ID, id) - } - - val pendingIntent = PendingIntent.getActivity( - context, 0, reminderIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE - ) - - val builder = NotificationCompat.Builder(context, ALARM_NOTIFICATION_CHANNEL_ID) - .setSmallIcon(R.drawable.ic_alarm_vector) - .setContentTitle(context.getString(org.fossify.commons.R.string.alarm)) - .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(NotificationCompat.CATEGORY_ALARM) - .setFullScreenIntent(pendingIntent, true) - - try { - notificationManager.notify(ALARM_NOTIF_ID, builder.build()) - } catch (e: Exception) { - context.showErrorToast(e) - } - } else { - Intent(context, ReminderActivity::class.java).apply { - addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - putExtra(ALARM_ID, id) - context.startActivity(this) + try { + Intent(context, AlarmService::class.java).apply { + putExtra(ALARM_ID, id) + if (isOreoPlus()) { + context.startForegroundService(this) + } else { + context.startService(this) } } + } catch (e: Exception) { + context.showErrorToast(e) } } - - @RequiresApi(Build.VERSION_CODES.O) - private fun oldNotificationChannelCleanup(notificationManager: NotificationManager) { - notificationManager.deleteNotificationChannel("Alarm") - } } diff --git a/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt b/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt index 0edd6dac..03dba11a 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/HideAlarmReceiver.kt @@ -3,10 +3,10 @@ package org.fossify.clock.receivers import android.content.BroadcastReceiver import android.content.Context import android.content.Intent -import org.fossify.clock.extensions.disableExpiredAlarm import org.fossify.clock.extensions.dbHelper import org.fossify.clock.extensions.deleteNotificationChannel -import org.fossify.clock.extensions.hideNotification +import org.fossify.clock.extensions.disableExpiredAlarm +import org.fossify.clock.extensions.stopAlarmService import org.fossify.clock.helpers.ALARM_ID import org.fossify.clock.helpers.ALARM_NOTIFICATION_CHANNEL_ID import org.fossify.commons.helpers.ensureBackgroundThread @@ -16,8 +16,7 @@ class HideAlarmReceiver : BroadcastReceiver() { val id = intent.getIntExtra(ALARM_ID, -1) val channelId = intent.getStringExtra(ALARM_NOTIFICATION_CHANNEL_ID) channelId?.let { context.deleteNotificationChannel(channelId) } - context.hideNotification(id) - + context.stopAlarmService() ensureBackgroundThread { val alarm = context.dbHelper.getAlarmWithId(id) if (alarm != null) { diff --git a/app/src/main/kotlin/org/fossify/clock/services/AlarmService.kt b/app/src/main/kotlin/org/fossify/clock/services/AlarmService.kt new file mode 100644 index 00000000..7a30c741 --- /dev/null +++ b/app/src/main/kotlin/org/fossify/clock/services/AlarmService.kt @@ -0,0 +1,217 @@ +package org.fossify.clock.services + +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent +import android.app.PendingIntent.FLAG_IMMUTABLE +import android.app.PendingIntent.FLAG_UPDATE_CURRENT +import android.app.Service +import android.content.Intent +import android.media.AudioAttributes +import android.media.AudioManager +import android.media.MediaPlayer +import android.os.Handler +import android.os.Looper +import android.os.VibrationEffect +import android.os.Vibrator +import androidx.core.app.NotificationCompat +import androidx.core.net.toUri +import org.fossify.clock.R +import org.fossify.clock.activities.ReminderActivity +import org.fossify.clock.extensions.config +import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.getFormattedTime +import org.fossify.clock.extensions.getHideAlarmPendingIntent +import org.fossify.clock.extensions.getSnoozePendingIntent +import org.fossify.clock.helpers.ALARM_ID +import org.fossify.clock.helpers.ALARM_NOTIFICATION_CHANNEL_ID +import org.fossify.clock.helpers.ALARM_NOTIF_ID +import org.fossify.clock.models.Alarm +import org.fossify.commons.extensions.notificationManager +import org.fossify.commons.helpers.SILENT +import org.fossify.commons.helpers.isOreoPlus + +/** + * Service responsible for sounding the alarms and vibrations. + * It also shows a notification with actions to dismiss or snooze an alarm. + * Totally based on the previous implementation in the [ReminderActivity]. + */ +class AlarmService : Service() { + + companion object { + private const val DEFAULT_ALARM_VOLUME = 7 + private const val INCREASE_VOLUME_DELAY = 300L + private const val MIN_ALARM_VOLUME_FOR_INCREASING_ALARMS = 1 + } + + private var alarm: Alarm? = null + private var audioManager: AudioManager? = null + private var initialAlarmVolume = DEFAULT_ALARM_VOLUME + private var mediaPlayer: MediaPlayer? = null + private var vibrator: Vibrator? = null + + private val autoDismissHandler = Handler(Looper.getMainLooper()) + private val increaseVolumeHandler = Handler(Looper.getMainLooper()) + + override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int { + val alarmId = intent?.getIntExtra(ALARM_ID, -1) ?: -1 + if (alarmId == -1) { + stopSelf() + return START_NOT_STICKY + } + + alarm = applicationContext.dbHelper.getAlarmWithId(alarmId) + if (alarm == null) { + stopSelf() + return START_NOT_STICKY + } + + val notification = buildNotification(alarm!!) + startForeground(ALARM_NOTIF_ID, notification) + startAlarmEffects(alarm!!) + startAutoDismiss(config.alarmMaxReminderSecs) + return START_STICKY + } + + private fun buildNotification(alarm: Alarm): Notification { + val channelId = ALARM_NOTIFICATION_CHANNEL_ID + if (isOreoPlus()) { + val channel = NotificationChannel( + channelId, + getString(org.fossify.commons.R.string.alarm), + NotificationManager.IMPORTANCE_HIGH + ).apply { + setBypassDnd(true) + setSound(null, null) + } + + notificationManager.createNotificationChannel(channel) + } + + val contentTitle = if (alarm.label.isEmpty()) { + getString(org.fossify.commons.R.string.alarm) + } else { + alarm.label + } + + val contentText = getFormattedTime( + passedSeconds = alarm.timeInMinutes * 60, + showSeconds = false, + makeAmPmSmaller = false + ) + + val reminderIntent = Intent(this, ReminderActivity::class.java).apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + putExtra(ALARM_ID, alarm.id) + } + + val pendingIntent = PendingIntent.getActivity( + this, 0, reminderIntent, FLAG_UPDATE_CURRENT or FLAG_IMMUTABLE + ) + + val dismissIntent = applicationContext.getHideAlarmPendingIntent(alarm, channelId) + val snoozeIntent = applicationContext.getSnoozePendingIntent(alarm) + + return NotificationCompat.Builder(this, channelId) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setSmallIcon(R.drawable.ic_alarm_vector) + .setContentIntent(pendingIntent) + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(NotificationCompat.CATEGORY_ALARM) + .setDefaults(NotificationCompat.DEFAULT_LIGHTS) + .addAction( + org.fossify.commons.R.drawable.ic_snooze_vector, + getString(org.fossify.commons.R.string.snooze), + snoozeIntent + ) + .addAction( + org.fossify.commons.R.drawable.ic_cross_vector, + getString(org.fossify.commons.R.string.dismiss), + dismissIntent + ) + .setDeleteIntent(dismissIntent) + .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) + .setFullScreenIntent(pendingIntent, true) + .build() + } + + private fun startAlarmEffects(alarm: Alarm) { + if (alarm.soundUri != SILENT) { + try { + val audioAttributes = AudioAttributes.Builder() + .setUsage(AudioAttributes.USAGE_ALARM) + .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED) + .build() + + mediaPlayer = MediaPlayer().apply { + setAudioAttributes(audioAttributes) + setDataSource(this@AlarmService, alarm.soundUri.toUri()) + isLooping = true + prepare() + start() + } + + if (config.increaseVolumeGradually) { + initialAlarmVolume = audioManager?.getStreamVolume( + AudioManager.STREAM_ALARM + ) ?: DEFAULT_ALARM_VOLUME + + scheduleVolumeIncrease( + lastVolume = MIN_ALARM_VOLUME_FOR_INCREASING_ALARMS.toFloat(), + maxVolume = initialAlarmVolume.toFloat(), + delay = 0 + ) + } + } catch (e: Exception) { + e.printStackTrace() + } + } + + if (alarm.vibrate && isOreoPlus()) { + vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator + val pattern = longArrayOf(500, 500) + vibrator?.vibrate(VibrationEffect.createWaveform(pattern, 0)) + } + } + + private fun scheduleVolumeIncrease(lastVolume: Float, maxVolume: Float, delay: Long) { + increaseVolumeHandler.postDelayed({ + val newVolume = (lastVolume + 0.1f).coerceAtMost(maxVolume) + audioManager?.setStreamVolume(AudioManager.STREAM_ALARM, newVolume.toInt(), 0) + if (newVolume < maxVolume) { + scheduleVolumeIncrease(newVolume, maxVolume, INCREASE_VOLUME_DELAY) + } + }, delay) + } + + private fun resetVolumeToInitialValue() { + if (config.increaseVolumeGradually) { + audioManager?.setStreamVolume(AudioManager.STREAM_ALARM, initialAlarmVolume, 0) + } + } + + private fun startAutoDismiss(durationSecs: Int) { + autoDismissHandler.postDelayed({ + stopSelf() + }, durationSecs * 1000L) + } + + override fun onDestroy() { + super.onDestroy() + mediaPlayer?.stop() + mediaPlayer?.release() + mediaPlayer = null + vibrator?.cancel() + vibrator = null + + // Clear any scheduled volume changes or auto-dismiss messages + increaseVolumeHandler.removeCallbacksAndMessages(null) + autoDismissHandler.removeCallbacksAndMessages(null) + resetVolumeToInitialValue() + } + + override fun onBind(intent: Intent?) = null +} diff --git a/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt b/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt index 68f3c473..356fb645 100644 --- a/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt +++ b/app/src/main/kotlin/org/fossify/clock/services/SnoozeService.kt @@ -4,8 +4,8 @@ import android.app.IntentService import android.content.Intent import org.fossify.clock.extensions.config import org.fossify.clock.extensions.dbHelper -import org.fossify.clock.extensions.hideNotification import org.fossify.clock.extensions.setupAlarmClock +import org.fossify.clock.extensions.stopAlarmService import org.fossify.clock.helpers.ALARM_ID import java.util.Calendar @@ -13,7 +13,7 @@ class SnoozeService : IntentService("Snooze") { override fun onHandleIntent(intent: Intent?) { val id = intent!!.getIntExtra(ALARM_ID, -1) val alarm = dbHelper.getAlarmWithId(id) ?: return - hideNotification(id) + stopAlarmService() setupAlarmClock( alarm = alarm, triggerTimeMillis = Calendar.getInstance()