diff --git a/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt b/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt index cc71aac6..3f4061a5 100644 --- a/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt +++ b/app/src/main/kotlin/org/fossify/clock/activities/ReminderActivity.kt @@ -1,30 +1,52 @@ package org.fossify.clock.activities import android.annotation.SuppressLint -import android.content.Context import android.content.Intent import android.content.res.Configuration import android.media.AudioManager import android.media.MediaPlayer -import android.net.Uri -import android.os.* +import android.os.Bundle +import android.os.Handler +import android.os.Looper +import android.os.VibrationEffect +import android.os.Vibrator import android.provider.AlarmClock import android.view.MotionEvent import android.view.WindowManager import android.view.animation.AnimationUtils +import androidx.core.net.toUri import org.fossify.clock.R import org.fossify.clock.databinding.ActivityReminderBinding -import org.fossify.clock.extensions.* +import org.fossify.clock.extensions.cancelAlarmClock +import org.fossify.clock.extensions.config +import org.fossify.clock.extensions.dbHelper +import org.fossify.clock.extensions.getFormattedTime +import org.fossify.clock.extensions.scheduleNextAlarm +import org.fossify.clock.extensions.setupAlarmClock +import org.fossify.clock.extensions.updateWidgets import org.fossify.clock.helpers.ALARM_ID import org.fossify.clock.helpers.ALARM_NOTIF_ID import org.fossify.clock.helpers.getPassedSeconds import org.fossify.clock.models.Alarm -import org.fossify.commons.extensions.* +import org.fossify.commons.extensions.applyColorFilter +import org.fossify.commons.extensions.beGone +import org.fossify.commons.extensions.getColoredDrawableWithColor +import org.fossify.commons.extensions.getProperBackgroundColor +import org.fossify.commons.extensions.getProperPrimaryColor +import org.fossify.commons.extensions.getProperTextColor +import org.fossify.commons.extensions.notificationManager +import org.fossify.commons.extensions.onGlobalLayout +import org.fossify.commons.extensions.performHapticFeedback +import org.fossify.commons.extensions.showPickSecondsDialog +import org.fossify.commons.extensions.updateTextColors +import org.fossify.commons.extensions.viewBinding import org.fossify.commons.helpers.MINUTE_SECONDS import org.fossify.commons.helpers.SILENT import org.fossify.commons.helpers.isOreoMr1Plus import org.fossify.commons.helpers.isOreoPlus import java.util.Calendar +import kotlin.math.max +import kotlin.math.min class ReminderActivity : SimpleActivity() { companion object { @@ -73,11 +95,25 @@ class ReminderActivity : SimpleActivity() { } binding.reminderTitle.text = label - binding.reminderText.text = if (isAlarmReminder) getFormattedTime(getPassedSeconds(), false, false) else getString(R.string.time_expired) + binding.reminderText.text = if (isAlarmReminder) { + getFormattedTime( + passedSeconds = getPassedSeconds(), + showSeconds = false, + makeAmPmSmaller = false + ) + } else { + getString(R.string.time_expired) + } + + val maxDuration = if (isAlarmReminder) { + config.alarmMaxReminderSecs + } else { + config.timerMaxReminderSecs + } - val maxDuration = if (isAlarmReminder) config.alarmMaxReminderSecs else config.timerMaxReminderSecs maxReminderDurationHandler.postDelayed({ finishActivity() + cancelNotification() }, maxDuration * 1000L) setupButtons() @@ -95,7 +131,12 @@ class ReminderActivity : SimpleActivity() { @SuppressLint("ClickableViewAccessibility") private fun setupAlarmButtons() { binding.reminderStop.beGone() - binding.reminderDraggableBackground.startAnimation(AnimationUtils.loadAnimation(this, R.anim.pulsing_animation)) + binding.reminderDraggableBackground.startAnimation( + AnimationUtils.loadAnimation( + this, + R.anim.pulsing_animation + ) + ) binding.reminderDraggableBackground.applyColorFilter(getProperPrimaryColor()) val textColor = getProperTextColor() @@ -136,7 +177,11 @@ class ReminderActivity : SimpleActivity() { } MotionEvent.ACTION_MOVE -> { - binding.reminderDraggable.x = Math.min(maxDragX, Math.max(minDragX, event.rawX - dragDownX)) + binding.reminderDraggable.x = min( + a = maxDragX, + b = max(minDragX, event.rawX - dragDownX) + ) + if (binding.reminderDraggable.x >= maxDragX - 50f) { if (!didVibrate) { binding.reminderDraggable.performHapticFeedback() @@ -144,9 +189,7 @@ class ReminderActivity : SimpleActivity() { finishActivity() } - if (isOreoPlus()) { - notificationManager.cancelAll() - } + cancelNotification() } else if (binding.reminderDraggable.x <= minDragX + 50f) { if (!didVibrate) { binding.reminderDraggable.performHapticFeedback() @@ -154,9 +197,7 @@ class ReminderActivity : SimpleActivity() { snoozeAlarm() } - if (isOreoPlus()) { - notificationManager.cancelAll() - } + cancelNotification() } } } @@ -165,8 +206,16 @@ class ReminderActivity : SimpleActivity() { } private fun setupTimerButtons() { - binding.reminderStop.background = resources.getColoredDrawableWithColor(R.drawable.circle_background_filled, getProperPrimaryColor()) - arrayOf(binding.reminderSnooze, binding.reminderDraggableBackground, binding.reminderDraggable, binding.reminderDismiss).forEach { + binding.reminderStop.background = resources.getColoredDrawableWithColor( + drawableId = R.drawable.circle_background_filled, + color = getProperPrimaryColor() + ) + arrayOf( + binding.reminderSnooze, + binding.reminderDraggableBackground, + binding.reminderDraggable, + binding.reminderDismiss + ).forEach { it.beGone() } @@ -176,14 +225,14 @@ class ReminderActivity : SimpleActivity() { } private fun setupEffects() { - audioManager = getSystemService(Context.AUDIO_SERVICE) as AudioManager + 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(Context.VIBRATOR_SERVICE) as Vibrator + vibrator = getSystemService(VIBRATOR_SERVICE) as Vibrator vibrator?.vibrate(VibrationEffect.createWaveform(pattern, 0)) }, 500) } @@ -198,14 +247,18 @@ class ReminderActivity : SimpleActivity() { try { mediaPlayer = MediaPlayer().apply { setAudioStreamType(AudioManager.STREAM_ALARM) - setDataSource(this@ReminderActivity, Uri.parse(soundUri)) + setDataSource(this@ReminderActivity, soundUri.toUri()) isLooping = true prepare() start() } if (config.increaseVolumeGradually) { - scheduleVolumeIncrease(MIN_ALARM_VOLUME_FOR_INCREASING_ALARMS.toFloat(), initialAlarmVolume!!.toFloat(), 0) + scheduleVolumeIncrease( + lastVolume = MIN_ALARM_VOLUME_FOR_INCREASING_ALARMS.toFloat(), + maxVolume = initialAlarmVolume!!.toFloat(), + delay = 0 + ) } } catch (e: Exception) { } @@ -253,7 +306,7 @@ class ReminderActivity : SimpleActivity() { vibrationHandler.removeCallbacksAndMessages(null) if (!finished) { finishActivity() - notificationManager.cancel(ALARM_NOTIF_ID) + cancelNotification() } else { destroyEffects() } @@ -278,10 +331,17 @@ class ReminderActivity : SimpleActivity() { } else if (config.useSameSnooze) { scheduleSnoozedAlarm(config.snoozeTime) } else { - showPickSecondsDialog(config.snoozeTime * MINUTE_SECONDS, true, cancelCallback = { finishActivity() }) { - config.snoozeTime = it / MINUTE_SECONDS - scheduleSnoozedAlarm(config.snoozeTime) - } + showPickSecondsDialog( + curSeconds = config.snoozeTime * MINUTE_SECONDS, + isSnoozePicker = true, + cancelCallback = { + finishActivity() + }, + callback = { + config.snoozeTime = it / MINUTE_SECONDS + scheduleSnoozedAlarm(config.snoozeTime) + } + ) } } @@ -323,9 +383,9 @@ class ReminderActivity : SimpleActivity() { private fun showOverLockscreen() { window.addFlags( WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or - WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or - WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or - WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON ) if (isOreoMr1Plus()) { @@ -333,4 +393,8 @@ class ReminderActivity : SimpleActivity() { setTurnScreenOn(true) } } + + private fun cancelNotification() { + notificationManager.cancel(ALARM_NOTIF_ID) + } } 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 8ab7a47b..ecb37431 100644 --- a/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt +++ b/app/src/main/kotlin/org/fossify/clock/extensions/Context.kt @@ -1,6 +1,10 @@ package org.fossify.clock.extensions -import android.app.* +import android.app.AlarmManager +import android.app.Notification +import android.app.NotificationChannel +import android.app.NotificationManager +import android.app.PendingIntent import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context @@ -8,7 +12,6 @@ import android.content.Intent import android.media.AudioAttributes import android.media.AudioManager.STREAM_ALARM import android.media.RingtoneManager -import android.net.Uri import android.os.Handler import android.os.Looper import android.os.PowerManager @@ -17,21 +20,73 @@ import android.text.style.RelativeSizeSpan import android.widget.Toast import androidx.core.app.AlarmManagerCompat 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.activities.SnoozeReminderActivity import org.fossify.clock.activities.SplashActivity import org.fossify.clock.databases.AppDatabase -import org.fossify.clock.helpers.* +import org.fossify.clock.helpers.ALARM_ID +import org.fossify.clock.helpers.ALARM_NOTIFICATION_CHANNEL_ID +import org.fossify.clock.helpers.Config +import org.fossify.clock.helpers.DBHelper +import org.fossify.clock.helpers.EARLY_ALARM_DISMISSAL_INTENT_ID +import org.fossify.clock.helpers.EDITED_TIME_ZONE_SEPARATOR +import org.fossify.clock.helpers.FORMAT_12H +import org.fossify.clock.helpers.FORMAT_24H +import org.fossify.clock.helpers.MyAnalogueTimeWidgetProvider +import org.fossify.clock.helpers.MyDigitalTimeWidgetProvider +import org.fossify.clock.helpers.NOTIFICATION_ID +import org.fossify.clock.helpers.OPEN_ALARMS_TAB_INTENT_ID +import org.fossify.clock.helpers.OPEN_STOPWATCH_TAB_INTENT_ID +import org.fossify.clock.helpers.OPEN_TAB +import org.fossify.clock.helpers.REMINDER_ACTIVITY_INTENT_ID +import org.fossify.clock.helpers.TAB_ALARM +import org.fossify.clock.helpers.TAB_STOPWATCH +import org.fossify.clock.helpers.TAB_TIMER +import org.fossify.clock.helpers.TIMER_ID +import org.fossify.clock.helpers.TODAY_BIT +import org.fossify.clock.helpers.TOMORROW_BIT +import org.fossify.clock.helpers.TimerHelper +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 import org.fossify.clock.models.MyTimeZone import org.fossify.clock.models.Timer import org.fossify.clock.models.TimerState -import org.fossify.clock.receivers.* +import org.fossify.clock.receivers.AlarmReceiver +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.SnoozeService -import org.fossify.commons.extensions.* -import org.fossify.commons.helpers.* +import org.fossify.commons.extensions.formatMinutesToTimeString +import org.fossify.commons.extensions.formatSecondsToTimeString +import org.fossify.commons.extensions.getDefaultAlarmSound +import org.fossify.commons.extensions.getLaunchIntent +import org.fossify.commons.extensions.getProperPrimaryColor +import org.fossify.commons.extensions.getSelectedDaysString +import org.fossify.commons.extensions.grantReadUriPermission +import org.fossify.commons.extensions.showErrorToast +import org.fossify.commons.extensions.toInt +import org.fossify.commons.extensions.toast +import org.fossify.commons.helpers.EVERY_DAY_BIT +import org.fossify.commons.helpers.FRIDAY_BIT +import org.fossify.commons.helpers.MINUTE_SECONDS +import org.fossify.commons.helpers.MONDAY_BIT +import org.fossify.commons.helpers.SATURDAY_BIT +import org.fossify.commons.helpers.SILENT +import org.fossify.commons.helpers.SUNDAY_BIT +import org.fossify.commons.helpers.THURSDAY_BIT +import org.fossify.commons.helpers.TUESDAY_BIT +import org.fossify.commons.helpers.WEDNESDAY_BIT +import org.fossify.commons.helpers.ensureBackgroundThread +import org.fossify.commons.helpers.isOreoPlus import java.text.SimpleDateFormat import java.util.Calendar import java.util.Locale @@ -350,7 +405,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add setBypassDnd(true) enableLights(true) lightColor = getProperPrimaryColor() - setSound(Uri.parse(soundUri), audioAttributes) + setSound(soundUri.toUri(), audioAttributes) if (!timer.vibrate) { vibrationPattern = longArrayOf(0L) @@ -375,7 +430,7 @@ fun Context.getTimerNotification(timer: Timer, pendingIntent: PendingIntent, add .setDefaults(Notification.DEFAULT_LIGHTS) .setCategory(Notification.CATEGORY_EVENT) .setAutoCancel(true) - .setSound(Uri.parse(soundUri), STREAM_ALARM) + .setSound(soundUri.toUri(), STREAM_ALARM) .setChannelId(channelId) .addAction( org.fossify.commons.R.drawable.ic_cross_vector, @@ -450,7 +505,7 @@ fun Context.getAlarmNotification(pendingIntent: PendingIntent, alarm: Alarm): No enableLights(true) lightColor = getProperPrimaryColor() enableVibration(alarm.vibrate) - setSound(Uri.parse(soundUri), audioAttributes) + setSound(soundUri.toUri(), audioAttributes) notificationManager.createNotificationChannel(this) } } @@ -475,7 +530,7 @@ fun Context.getAlarmNotification(pendingIntent: PendingIntent, alarm: Alarm): No .setVisibility(NotificationCompat.VISIBILITY_PUBLIC) if (soundUri != SILENT) { - builder.setSound(Uri.parse(soundUri), STREAM_ALARM) + builder.setSound(soundUri.toUri(), STREAM_ALARM) } if (alarm.vibrate) { 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 b06459c0..bf5b39af 100644 --- a/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt +++ b/app/src/main/kotlin/org/fossify/clock/receivers/AlarmReceiver.kt @@ -3,20 +3,28 @@ 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.* +import org.fossify.clock.extensions.config +import org.fossify.clock.extensions.dbHelper +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.commons.extensions.showErrorToast import org.fossify.commons.helpers.isOreoPlus @@ -26,30 +34,40 @@ class AlarmReceiver : BroadcastReceiver() { val id = intent.getIntExtra(ALARM_ID, -1) val alarm = context.dbHelper.getAlarmWithId(id) ?: return - context.hideNotification(EARLY_ALARM_NOTIF_ID) // hide early dismissal notification if not already dismissed + // Hide early dismissal notification if not already dismissed + context.hideNotification(EARLY_ALARM_NOTIF_ID) if (context.isScreenOn()) { context.showAlarmNotification(alarm) - Handler().postDelayed({ + Handler(Looper.getMainLooper()).postDelayed({ context.hideNotification(id) }, context.config.alarmMaxReminderSecs * 1000L) } else { if (isOreoPlus()) { - - val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + val notificationManager = context.notificationManager if (notificationManager.getNotificationChannel(ALARM_NOTIFICATION_CHANNEL_ID) == null) { - oldNotificationChannelCleanup(notificationManager) // cleans up previous notification channel that had sound properties - NotificationChannel(ALARM_NOTIFICATION_CHANNEL_ID, "Alarm", NotificationManager.IMPORTANCE_HIGH).apply { + // 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 pendingIntent = PendingIntent.getActivity(context, 0, Intent(context, ReminderActivity::class.java).apply { + val reminderIntent = Intent(context, ReminderActivity::class.java).apply { addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) putExtra(ALARM_ID, id) - }, PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE) + } + + 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)