mirror of
https://github.com/SimpleMobileTools/Simple-Clock.git
synced 2025-04-17 03:37:26 +02:00
357 lines
15 KiB
Kotlin
357 lines
15 KiB
Kotlin
package com.simplemobiletools.clock.extensions
|
|
|
|
import android.annotation.SuppressLint
|
|
import android.app.*
|
|
import android.appwidget.AppWidgetManager
|
|
import android.content.ComponentName
|
|
import android.content.Context
|
|
import android.content.Intent
|
|
import android.media.AudioManager
|
|
import android.media.RingtoneManager
|
|
import android.net.Uri
|
|
import android.os.PowerManager
|
|
import android.provider.Settings
|
|
import android.support.v4.app.AlarmManagerCompat
|
|
import android.support.v4.app.NotificationCompat
|
|
import android.text.SpannableString
|
|
import android.text.style.RelativeSizeSpan
|
|
import android.widget.Toast
|
|
import com.simplemobiletools.clock.R
|
|
import com.simplemobiletools.clock.activities.ReminderActivity
|
|
import com.simplemobiletools.clock.activities.SnoozeReminderActivity
|
|
import com.simplemobiletools.clock.activities.SplashActivity
|
|
import com.simplemobiletools.clock.helpers.*
|
|
import com.simplemobiletools.clock.models.Alarm
|
|
import com.simplemobiletools.clock.models.MyTimeZone
|
|
import com.simplemobiletools.clock.receivers.AlarmReceiver
|
|
import com.simplemobiletools.clock.receivers.DateTimeWidgetUpdateReceiver
|
|
import com.simplemobiletools.clock.receivers.HideAlarmReceiver
|
|
import com.simplemobiletools.clock.receivers.HideTimerReceiver
|
|
import com.simplemobiletools.clock.services.SnoozeService
|
|
import com.simplemobiletools.commons.extensions.formatMinutesToTimeString
|
|
import com.simplemobiletools.commons.extensions.getAdjustedPrimaryColor
|
|
import com.simplemobiletools.commons.extensions.toast
|
|
import com.simplemobiletools.commons.helpers.isKitkatPlus
|
|
import com.simplemobiletools.commons.helpers.isLollipopPlus
|
|
import com.simplemobiletools.commons.helpers.isOreoPlus
|
|
import java.util.*
|
|
import kotlin.math.pow
|
|
|
|
val Context.config: Config get() = Config.newInstance(applicationContext)
|
|
|
|
val Context.dbHelper: DBHelper get() = DBHelper.newInstance(applicationContext)
|
|
|
|
fun Context.getFormattedDate(calendar: Calendar): String {
|
|
val dayOfWeek = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7 // make sure index 0 means monday
|
|
val dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH)
|
|
val month = calendar.get(Calendar.MONTH)
|
|
|
|
val dayString = resources.getStringArray(R.array.week_days_short)[dayOfWeek]
|
|
val monthString = resources.getStringArray(R.array.months)[month]
|
|
return "$dayString, $dayOfMonth $monthString"
|
|
}
|
|
|
|
fun Context.getEditedTimeZonesMap(): HashMap<Int, String> {
|
|
val editedTimeZoneTitles = config.editedTimeZoneTitles
|
|
val editedTitlesMap = HashMap<Int, String>()
|
|
editedTimeZoneTitles.forEach {
|
|
val parts = it.split(EDITED_TIME_ZONE_SEPARATOR.toRegex(), 2)
|
|
editedTitlesMap[parts[0].toInt()] = parts[1]
|
|
}
|
|
return editedTitlesMap
|
|
}
|
|
|
|
fun Context.getAllTimeZonesModified(): ArrayList<MyTimeZone> {
|
|
val timeZones = getAllTimeZones()
|
|
val editedTitlesMap = getEditedTimeZonesMap()
|
|
timeZones.forEach {
|
|
if (editedTitlesMap.keys.contains(it.id)) {
|
|
it.title = editedTitlesMap[it.id]!!
|
|
} else {
|
|
it.title = it.title.substring(it.title.indexOf(' ')).trim()
|
|
}
|
|
}
|
|
return timeZones
|
|
}
|
|
|
|
fun Context.getModifiedTimeZoneTitle(id: Int) = getAllTimeZonesModified().firstOrNull { it.id == id }?.title ?: getDefaultTimeZoneTitle(id)
|
|
|
|
fun Context.getDefaultAlarmUri() = RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
|
|
|
|
fun Context.getDefaultAlarmTitle() = RingtoneManager.getRingtone(this, getDefaultAlarmUri())?.getTitle(this) ?: getString(R.string.alarm)
|
|
|
|
fun Context.createNewAlarm(timeInMinutes: Int, weekDays: Int) = Alarm(0, timeInMinutes, weekDays, false, false, getDefaultAlarmTitle(), getDefaultAlarmUri().toString(), "")
|
|
|
|
fun Context.scheduleNextAlarm(alarm: Alarm, showToast: Boolean) {
|
|
val calendar = Calendar.getInstance()
|
|
calendar.firstDayOfWeek = Calendar.MONDAY
|
|
for (i in 0..7) {
|
|
val currentDay = (calendar.get(Calendar.DAY_OF_WEEK) + 5) % 7
|
|
val isCorrectDay = alarm.days and 2.0.pow(currentDay).toInt() != 0
|
|
val currentTimeInMinutes = calendar.get(Calendar.HOUR_OF_DAY) * 60 + calendar.get(Calendar.MINUTE)
|
|
if (isCorrectDay && (alarm.timeInMinutes > currentTimeInMinutes || i > 0)) {
|
|
val triggerInMinutes = alarm.timeInMinutes - currentTimeInMinutes + (i * DAY_MINUTES)
|
|
setupAlarmClock(alarm, triggerInMinutes * 60 - calendar.get(Calendar.SECOND))
|
|
|
|
if (showToast) {
|
|
showRemainingTimeMessage(triggerInMinutes)
|
|
}
|
|
break
|
|
} else {
|
|
calendar.add(Calendar.DAY_OF_MONTH, 1)
|
|
}
|
|
}
|
|
}
|
|
|
|
fun Context.showRemainingTimeMessage(totalMinutes: Int) {
|
|
val fullString = String.format(getString(R.string.alarm_goes_off_in), formatMinutesToTimeString(totalMinutes))
|
|
toast(fullString, Toast.LENGTH_LONG)
|
|
}
|
|
|
|
fun Context.setupAlarmClock(alarm: Alarm, triggerInSeconds: Int) {
|
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
val targetMS = System.currentTimeMillis() + triggerInSeconds * 1000
|
|
AlarmManagerCompat.setAlarmClock(alarmManager, targetMS, getOpenAlarmTabIntent(), getAlarmIntent(alarm))
|
|
}
|
|
|
|
fun Context.getOpenAlarmTabIntent(): PendingIntent {
|
|
val intent = Intent(this, SplashActivity::class.java)
|
|
intent.putExtra(OPEN_TAB, TAB_ALARM)
|
|
return PendingIntent.getActivity(this, OPEN_ALARMS_TAB_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
|
|
fun Context.getOpenTimerTabIntent(): PendingIntent {
|
|
val intent = Intent(this, SplashActivity::class.java)
|
|
intent.putExtra(OPEN_TAB, TAB_TIMER)
|
|
return PendingIntent.getActivity(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
|
|
fun Context.getAlarmIntent(alarm: Alarm): PendingIntent {
|
|
val intent = Intent(this, AlarmReceiver::class.java)
|
|
intent.putExtra(ALARM_ID, alarm.id)
|
|
return PendingIntent.getBroadcast(this, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
|
|
fun Context.cancelAlarmClock(alarm: Alarm) {
|
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
alarmManager.cancel(getAlarmIntent(alarm))
|
|
}
|
|
|
|
fun Context.hideNotification(id: Int) {
|
|
val manager = applicationContext.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
manager.cancel(id)
|
|
}
|
|
|
|
fun Context.hideTimerNotification() = hideNotification(TIMER_NOTIF_ID)
|
|
|
|
fun Context.updateWidgets() {
|
|
val widgetsCnt = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java))
|
|
if (widgetsCnt.isNotEmpty()) {
|
|
val ids = intArrayOf(R.xml.widget_date_time_info)
|
|
Intent(applicationContext, MyWidgetDateTimeProvider::class.java).apply {
|
|
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
|
|
putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, ids)
|
|
sendBroadcast(this)
|
|
}
|
|
}
|
|
}
|
|
|
|
@SuppressLint("NewApi")
|
|
fun Context.scheduleNextWidgetUpdate() {
|
|
val widgetsCnt = AppWidgetManager.getInstance(applicationContext).getAppWidgetIds(ComponentName(applicationContext, MyWidgetDateTimeProvider::class.java))
|
|
if (widgetsCnt.isEmpty()) {
|
|
return
|
|
}
|
|
|
|
val intent = Intent(this, DateTimeWidgetUpdateReceiver::class.java)
|
|
val pendingIntent = PendingIntent.getBroadcast(this, UPDATE_WIDGET_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
|
|
val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
|
|
val triggerAtMillis = System.currentTimeMillis() + getMSTillNextMinute()
|
|
when {
|
|
isKitkatPlus() -> alarmManager.setExact(AlarmManager.RTC, triggerAtMillis, pendingIntent)
|
|
else -> alarmManager.set(AlarmManager.RTC, triggerAtMillis, pendingIntent)
|
|
}
|
|
}
|
|
|
|
fun Context.getFormattedTime(passedSeconds: Int, showSeconds: Boolean, makeAmPmSmaller: Boolean): SpannableString {
|
|
val use24HourFormat = config.use24HourFormat
|
|
val hours = (passedSeconds / 3600) % 24
|
|
val minutes = (passedSeconds / 60) % 60
|
|
val seconds = passedSeconds % 60
|
|
|
|
return if (!use24HourFormat) {
|
|
val formattedTime = formatTo12HourFormat(showSeconds, hours, minutes, seconds)
|
|
val spannableTime = SpannableString(formattedTime)
|
|
val amPmMultiplier = if (makeAmPmSmaller) 0.4f else 1f
|
|
spannableTime.setSpan(RelativeSizeSpan(amPmMultiplier), spannableTime.length - 5, spannableTime.length, 0)
|
|
spannableTime
|
|
} else {
|
|
val formattedTime = formatTime(showSeconds, use24HourFormat, hours, minutes, seconds)
|
|
SpannableString(formattedTime)
|
|
}
|
|
}
|
|
|
|
fun Context.formatTo12HourFormat(showSeconds: Boolean, hours: Int, minutes: Int, seconds: Int): String {
|
|
val appendable = getString(if (hours >= 12) R.string.p_m else R.string.a_m)
|
|
val newHours = if (hours == 0 || hours == 12) 12 else hours % 12
|
|
return "${formatTime(showSeconds, false, newHours, minutes, seconds)} $appendable"
|
|
}
|
|
|
|
fun Context.getNextAlarm() = Settings.System.getString(contentResolver, Settings.System.NEXT_ALARM_FORMATTED) ?: ""
|
|
|
|
fun Context.rescheduleEnabledAlarms() {
|
|
dbHelper.getEnabledAlarms().forEach {
|
|
scheduleNextAlarm(it, false)
|
|
}
|
|
}
|
|
|
|
fun Context.isScreenOn() = (getSystemService(Context.POWER_SERVICE) as PowerManager).isScreenOn
|
|
|
|
fun Context.showAlarmNotification(alarm: Alarm, addDeleteIntent: Boolean) {
|
|
val pendingIntent = getOpenAlarmTabIntent()
|
|
val notification = getAlarmNotification(pendingIntent, alarm, addDeleteIntent)
|
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
notificationManager.notify(alarm.id, notification)
|
|
scheduleNextAlarm(alarm, false)
|
|
}
|
|
|
|
fun Context.showTimerNotification(addDeleteIntent: Boolean) {
|
|
val pendingIntent = getOpenTimerTabIntent()
|
|
val notification = getTimerNotification(pendingIntent, addDeleteIntent)
|
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
notificationManager.notify(TIMER_NOTIF_ID, notification)
|
|
}
|
|
|
|
@SuppressLint("NewApi")
|
|
fun Context.getTimerNotification(pendingIntent: PendingIntent, addDeleteIntent: Boolean): Notification {
|
|
val channelId = "timer_channel"
|
|
if (isOreoPlus()) {
|
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
val name = getString(R.string.timer)
|
|
val importance = NotificationManager.IMPORTANCE_HIGH
|
|
NotificationChannel(channelId, name, importance).apply {
|
|
enableLights(true)
|
|
lightColor = getAdjustedPrimaryColor()
|
|
enableVibration(config.timerVibrate)
|
|
notificationManager.createNotificationChannel(this)
|
|
}
|
|
}
|
|
|
|
grantReadUriPermission(config.timerSoundUri)
|
|
val reminderActivityIntent = getReminderActivityIntent()
|
|
val builder = NotificationCompat.Builder(this)
|
|
.setContentTitle(getString(R.string.timer))
|
|
.setContentText(getString(R.string.time_expired))
|
|
.setSmallIcon(R.drawable.ic_timer)
|
|
.setContentIntent(pendingIntent)
|
|
.setPriority(Notification.PRIORITY_HIGH)
|
|
.setDefaults(Notification.DEFAULT_LIGHTS)
|
|
.setAutoCancel(true)
|
|
.setSound(Uri.parse(config.timerSoundUri), AudioManager.STREAM_SYSTEM)
|
|
.setChannelId(channelId)
|
|
.addAction(R.drawable.ic_cross, getString(R.string.dismiss), if (addDeleteIntent) reminderActivityIntent else getHideTimerPendingIntent())
|
|
|
|
if (addDeleteIntent) {
|
|
builder.setDeleteIntent(reminderActivityIntent)
|
|
}
|
|
|
|
if (isLollipopPlus()) {
|
|
builder.setVisibility(Notification.VISIBILITY_PUBLIC)
|
|
}
|
|
|
|
if (config.timerVibrate) {
|
|
val vibrateArray = LongArray(2) { 500 }
|
|
builder.setVibrate(vibrateArray)
|
|
}
|
|
|
|
val notification = builder.build()
|
|
notification.flags = notification.flags or Notification.FLAG_INSISTENT
|
|
return notification
|
|
}
|
|
|
|
fun Context.grantReadUriPermission(uriString: String) {
|
|
try {
|
|
// ensure custom reminder sounds play well
|
|
grantUriPermission("com.android.systemui", Uri.parse(uriString), Intent.FLAG_GRANT_READ_URI_PERMISSION)
|
|
} catch (ignored: Exception) {
|
|
}
|
|
}
|
|
|
|
fun Context.getHideTimerPendingIntent(): PendingIntent {
|
|
val intent = Intent(this, HideTimerReceiver::class.java)
|
|
return PendingIntent.getBroadcast(this, TIMER_NOTIF_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
|
|
fun Context.getHideAlarmPendingIntent(alarm: Alarm): PendingIntent {
|
|
val intent = Intent(this, HideAlarmReceiver::class.java)
|
|
intent.putExtra(ALARM_ID, alarm.id)
|
|
return PendingIntent.getBroadcast(this, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
|
|
@SuppressLint("NewApi")
|
|
fun Context.getAlarmNotification(pendingIntent: PendingIntent, alarm: Alarm, addDeleteIntent: Boolean): Notification {
|
|
val channelId = "alarm_channel"
|
|
val label = if (alarm.label.isNotEmpty()) alarm.label else getString(R.string.alarm)
|
|
if (isOreoPlus()) {
|
|
val notificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
|
val name = label
|
|
val importance = NotificationManager.IMPORTANCE_HIGH
|
|
NotificationChannel(channelId, name, importance).apply {
|
|
enableLights(true)
|
|
lightColor = getAdjustedPrimaryColor()
|
|
enableVibration(alarm.vibrate)
|
|
notificationManager.createNotificationChannel(this)
|
|
}
|
|
}
|
|
|
|
grantReadUriPermission(alarm.soundUri)
|
|
val reminderActivityIntent = getReminderActivityIntent()
|
|
val builder = NotificationCompat.Builder(this)
|
|
.setContentTitle(label)
|
|
.setContentText(getFormattedTime(getPassedSeconds(), false, false))
|
|
.setSmallIcon(R.drawable.ic_alarm)
|
|
.setContentIntent(pendingIntent)
|
|
.setPriority(Notification.PRIORITY_HIGH)
|
|
.setDefaults(Notification.DEFAULT_LIGHTS)
|
|
.setAutoCancel(true)
|
|
.setSound(Uri.parse(alarm.soundUri), AudioManager.STREAM_ALARM)
|
|
.setChannelId(channelId)
|
|
.addAction(R.drawable.ic_cross, getString(R.string.dismiss), if (addDeleteIntent) reminderActivityIntent else getHideAlarmPendingIntent(alarm))
|
|
.addAction(R.drawable.ic_snooze, getString(R.string.snooze), getSnoozePendingIntent(alarm, addDeleteIntent))
|
|
|
|
if (addDeleteIntent) {
|
|
builder.setDeleteIntent(reminderActivityIntent)
|
|
}
|
|
|
|
if (isLollipopPlus()) {
|
|
builder.setVisibility(Notification.VISIBILITY_PUBLIC)
|
|
}
|
|
|
|
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, hideReminderActivity: Boolean): PendingIntent {
|
|
val snoozeClass = if (config.useSameSnooze) SnoozeService::class.java else SnoozeReminderActivity::class.java
|
|
val intent = Intent(this, snoozeClass).setAction("Snooze")
|
|
intent.putExtra(ALARM_ID, alarm.id)
|
|
intent.putExtra(HIDE_REMINDER_ACTIVITY, hideReminderActivity)
|
|
return if (config.useSameSnooze) {
|
|
PendingIntent.getService(this, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
} else {
|
|
PendingIntent.getActivity(this, alarm.id, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|
|
}
|
|
|
|
fun Context.getReminderActivityIntent(): PendingIntent {
|
|
val intent = Intent(this, ReminderActivity::class.java)
|
|
return PendingIntent.getActivity(this, REMINDER_ACTIVITY_INTENT_ID, intent, PendingIntent.FLAG_UPDATE_CURRENT)
|
|
}
|