Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -42,14 +42,29 @@
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<receiver
android:name=".BootReceiver"
android:enabled="true"
android:exported="false"
android:directBootAware="true">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED"/>
<action android:name="android.intent.action.MY_PACKAGE_REPLACED"/>
<action android:name="android.intent.action.QUICKBOOT_POWERON"/>
<action android:name="com.htc.intent.action.QUICKBOOT_POWERON"/>
</intent-filter>
</receiver>
<receiver
android:name=".AlarmReceiver"
android:enabled="true"
android:exported="false" />
android:exported="false"
android:directBootAware="true" />
<service
android:name=".AlarmPlaybackService"
android:enabled="true"
android:exported="false"
android:directBootAware="true"
android:foregroundServiceType="mediaPlayback" />
<meta-data
android:name="flutterEmbedding"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -77,21 +77,35 @@ class AlarmPlaybackService : Service() {
private fun startAudio(sound: String) {
stopAudioAndVibration()

val uri = parseSoundUri(sound)
?: RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
?: RingtoneManager.getDefaultUri(RingtoneManager.TYPE_NOTIFICATION)

mediaPlayer = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
.build(),
)
isLooping = true
setDataSource(this@AlarmPlaybackService, uri)
prepare()
start()
val candidateUri = parseSoundUri(sound)
val fallbackUri =
RingtoneManager.getDefaultUri(RingtoneManager.TYPE_ALARM)
?: RingtoneManager.getDefaultUri(
RingtoneManager.TYPE_NOTIFICATION,
)

val urisToTry = listOfNotNull(candidateUri, fallbackUri).distinct()
for (uri in urisToTry) {
try {
mediaPlayer = MediaPlayer().apply {
setAudioAttributes(
AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_ALARM)
.setContentType(
AudioAttributes.CONTENT_TYPE_SONIFICATION,
)
.build(),
)
isLooping = true
setDataSource(this@AlarmPlaybackService, uri)
prepare()
start()
}
return
} catch (_: Exception) {
mediaPlayer?.release()
mediaPlayer = null
}
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package dev.kashifkhan.drawbell

import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent

class BootReceiver : BroadcastReceiver() {
private companion object {
private const val ACTION_QUICKBOOT_POWERON =
"android.intent.action.QUICKBOOT_POWERON"
private const val ACTION_HTC_QUICKBOOT_POWERON =
"com.htc.intent.action.QUICKBOOT_POWERON"
private const val ACTION_MY_PACKAGE_REPLACED =
"android.intent.action.MY_PACKAGE_REPLACED"
}

override fun onReceive(context: Context, intent: Intent) {
val action = intent.action ?: return
if (
action != Intent.ACTION_BOOT_COMPLETED &&
action != Intent.ACTION_LOCKED_BOOT_COMPLETED &&
action != ACTION_MY_PACKAGE_REPLACED &&
action != ACTION_QUICKBOOT_POWERON &&
action != ACTION_HTC_QUICKBOOT_POWERON
) return

val isLockedBoot = action == Intent.ACTION_LOCKED_BOOT_COMPLETED
val now = System.currentTimeMillis()
val ids = NativeAlarmStore.getAlarmIds(context)
for (id in ids) {
val entry = NativeAlarmStore.getEntry(
context = context,
id = id,
allowLegacyMigration = !isLockedBoot,
) ?: continue
var entryToSchedule = entry
if (entryToSchedule.scheduledTimeMillis <= now) {
val rebuiltFromStored =
NativeAlarmStore.rebuildFromStoredMetadata(entryToSchedule)
if (rebuiltFromStored != null && rebuiltFromStored.scheduledTimeMillis > now) {
entryToSchedule = rebuiltFromStored
} else {
if (isLockedBoot) {
continue
}
val rebuilt = NativeAlarmStore.rebuildEntryFromFlutterPrefs(
context = context,
id = id,
payload = entryToSchedule.payload,
) ?: continue
if (rebuilt.scheduledTimeMillis <= now) {
continue
}
entryToSchedule = rebuilt
}
}
NativeAlarmScheduler.schedule(
context = context,
id = entryToSchedule.id,
title = entryToSchedule.title,
body = entryToSchedule.body,
payload = entryToSchedule.payload,
sound = entryToSchedule.sound,
scheduledTimeMillis = entryToSchedule.scheduledTimeMillis,
hour = entryToSchedule.hour,
minute = entryToSchedule.minute,
repeatDays = entryToSchedule.repeatDays,
scheduledDate = entryToSchedule.scheduledDate,
)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,19 @@ class MainActivity : FlutterActivity() {
val body = args["body"] as? String ?: "Alarm — draw to dismiss!"
val payload = args["payload"] as? String ?: ""
val sound = args["sound"] as? String ?: "default"
val hour = (args["hour"] as? Number)?.toInt() ?: run {
result.error("INVALID_ARGS", "Missing hour", null)
return
}
val minute = (args["minute"] as? Number)?.toInt() ?: run {
result.error("INVALID_ARGS", "Missing minute", null)
return
}
val repeatDays =
(args["repeatDays"] as? List<*>)
?.mapNotNull { (it as? Number)?.toInt() }
?: emptyList()
val scheduledDate = args["scheduledDate"] as? String
val scheduledTimeMillis =
(args["scheduledTimeMillis"] as? Number)?.toLong() ?: run {
result.error("INVALID_ARGS", "Missing scheduled time", null)
Expand All @@ -185,6 +198,10 @@ class MainActivity : FlutterActivity() {
payload = payload,
sound = sound,
scheduledTimeMillis = scheduledTimeMillis,
hour = hour,
minute = minute,
repeatDays = repeatDays,
scheduledDate = scheduledDate,
)
result.success(null)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,10 @@ object NativeAlarmScheduler {
payload: String,
sound: String,
scheduledTimeMillis: Long,
hour: Int,
minute: Int,
repeatDays: List<Int>,
scheduledDate: String?,
) {
val alarmManager =
context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
Expand All @@ -36,7 +40,19 @@ object NativeAlarmScheduler {
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)

NativeAlarmStore.putAlarm(context, id, payload)
NativeAlarmStore.putAlarm(
context = context,
id = id,
title = title,
body = body,
payload = payload,
sound = sound,
scheduledTimeMillis = scheduledTimeMillis,
hour = hour,
minute = minute,
repeatDays = repeatDays,
scheduledDate = scheduledDate,
)

alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC_WAKEUP,
Expand Down
Loading
Loading