feat: Add boot notification

This commit is contained in:
Myzel394 2023-10-28 17:52:32 +02:00
parent 60fc16f2c4
commit 2e9f1c7ade
No known key found for this signature in database
GPG Key ID: 79CC92F37B3E1A2B
5 changed files with 127 additions and 29 deletions

View File

@ -9,18 +9,30 @@ import androidx.annotation.RequiresApi
object NotificationHelper {
const val RECORDER_CHANNEL_ID = "recorder"
const val RECORDER_CHANNEL_NOTIFICATION_ID = 1
const val BOOT_CHANNEL_ID = "boot"
const val BOOT_CHANNEL_NOTIFICATION_ID = 2
@RequiresApi(Build.VERSION_CODES.O)
fun createChannels(context: Context) {
val channel = NotificationChannel(
RECORDER_CHANNEL_ID,
context.resources.getString(R.string.notificationChannels_recorder_name),
android.app.NotificationManager.IMPORTANCE_LOW,
)
channel.description = context.resources.getString(R.string.notificationChannels_recorder_description)
val notificationManager = context.getSystemService(NotificationManager::class.java)
notificationManager.createNotificationChannel(channel)
notificationManager.createNotificationChannel(
NotificationChannel(
RECORDER_CHANNEL_ID,
context.getString(R.string.notificationChannels_recorder_name),
NotificationManager.IMPORTANCE_LOW,
).apply {
description = context.getString(R.string.notificationChannels_recorder_description)
}
)
notificationManager.createNotificationChannel(
NotificationChannel(
BOOT_CHANNEL_ID,
context.getString(R.string.notificationChannels_boot_name),
NotificationManager.IMPORTANCE_LOW,
).apply {
description = context.getString(R.string.notificationChannels_boot_description)
}
)
}
}

View File

@ -19,7 +19,7 @@ data class AppSettings(
val showAdvancedSettings: Boolean = false,
val theme: Theme = Theme.SYSTEM,
val lastRecording: RecordingInformation? = null,
val bootBehavior: BootBehavior? = BootBehavior.START_RECORDING,
val bootBehavior: BootBehavior? = BootBehavior.SHOW_NOTIFICATION,
) {
fun setShowAdvancedSettings(showAdvancedSettings: Boolean): AppSettings {
return copy(showAdvancedSettings = showAdvancedSettings)
@ -56,7 +56,13 @@ data class AppSettings(
}
enum class BootBehavior {
// Always start recording, no matter if it was interrupted or not
START_RECORDING,
// Only start recording if it was interrupted
CONTINUE_RECORDING,
// Show a notification if interrupted
SHOW_NOTIFICATION,
}

View File

@ -1,42 +1,118 @@
package app.myzel394.alibi.receivers
import android.app.NotificationManager
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.content.ServiceConnection
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import app.myzel394.alibi.MainActivity
import app.myzel394.alibi.NotificationHelper
import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.helpers.AudioRecorderExporter
import app.myzel394.alibi.services.AudioRecorderService
import app.myzel394.alibi.services.RecorderNotificationHelper
import app.myzel394.alibi.services.RecorderService
import app.myzel394.alibi.ui.enums.Screen
import app.myzel394.alibi.ui.models.AudioRecorderModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.flow.collectLatest
import kotlinx.coroutines.launch
import kotlinx.serialization.json.Json
class BootReceiver : BroadcastReceiver() {
private var job = SupervisorJob()
private var scope = CoroutineScope(Dispatchers.IO + job)
private fun startRecording(context: Context, settings: AppSettings) {
val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder ->
recorder.startRecording()
}
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
val intent = Intent(context, AudioRecorderService::class.java).apply {
action = "init"
if (settings.notificationSettings != null) {
putExtra(
"notificationDetails",
Json.encodeToString(
RecorderNotificationHelper.NotificationDetails.serializer(),
RecorderNotificationHelper.NotificationDetails.fromNotificationSettings(
context,
settings.notificationSettings
)
),
)
}
}
ContextCompat.startForegroundService(context, intent)
context.bindService(intent, connection, 0)
}
private fun showNotification(context: Context) {
if (!AudioRecorderExporter.hasRecordingsAvailable(context)) {
// Nothing interrupted, so no notification needs to be shown
return
}
val notification = NotificationCompat.Builder(context, NotificationHelper.BOOT_CHANNEL_ID)
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_REMINDER)
.setSmallIcon(R.drawable.launcher_monochrome_noopacity)
.setContentIntent(
PendingIntent.getActivity(
context,
0,
Intent(context, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE,
)
)
.setOnlyAlertOnce(true)
.setContentTitle(context.getString(R.string.notification_boot_title))
.setContentText(context.getString(R.string.notification_boot_message))
.build()
val notificationManager =
context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.notify(NotificationHelper.BOOT_CHANNEL_NOTIFICATION_ID, notification)
}
override fun onReceive(context: Context?, intent: Intent?) {
println("Received new intent: ${intent?.action}")
if (intent?.action != Intent.ACTION_BOOT_COMPLETED || context == null) {
return
}
if (intent?.action == Intent.ACTION_BOOT_COMPLETED) {
println("Starting service")
scope.launch {
context.dataStore.data.collectLatest { settings ->
when (settings.bootBehavior) {
AppSettings.BootBehavior.CONTINUE_RECORDING -> {
if (AudioRecorderExporter.hasRecordingsAvailable(context)) {
startRecording(context, settings)
}
}
val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder ->
recorder.startRecording()
AppSettings.BootBehavior.START_RECORDING -> startRecording(context, settings)
AppSettings.BootBehavior.SHOW_NOTIFICATION -> showNotification(context)
null -> {
// Nothing to do
}
}
override fun onServiceDisconnected(arg0: ComponentName) {
}
}
val intent = Intent(context, AudioRecorderService::class.java).apply {
action = "initStart"
}
println("Starting service checking context")
if (context != null) {
println("Starting service with context")
ContextCompat.startForegroundService(context, intent)
}
}
}

View File

@ -125,7 +125,7 @@ class AudioRecorderModel : ViewModel() {
}
}
ContextCompat.startForegroundService(context, intent)
context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
context.bindService(intent, connection, 0)
}
fun stopRecording(context: Context) {

View File

@ -111,4 +111,8 @@
<string name="ui_settings_option_deleteRecordingsImmediately_description">If enabled, Alibi will immediately delete recordings after you have saved the file.</string>
<string name="ui_settings_bootBehavior_title">Boot Behavior</string>
<string name="ui_settings_bootBehavior_values_SHOW_NOTIFICATION_title">Show a notification</string>
<string name="notificationChannels_boot_name">Boot notification</string>
<string name="notificationChannels_boot_description">If enabled, you\'ll be informed that your recording was interrupted</string>
<string name="notification_boot_title">Alibi was interrupted</string>
<string name="notification_boot_message">Your device restarted and your recording has been interrupted</string>
</resources>