diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt new file mode 100644 index 0000000..469a8a6 --- /dev/null +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt @@ -0,0 +1,121 @@ +package app.myzel394.alibi.services + +import android.app.Notification +import android.app.PendingIntent +import android.content.Context +import android.content.Intent +import androidx.core.app.NotificationCompat +import app.myzel394.alibi.MainActivity +import app.myzel394.alibi.NotificationHelper +import app.myzel394.alibi.R +import app.myzel394.alibi.enums.RecorderState +import java.time.LocalDateTime +import java.time.ZoneId +import java.util.Calendar +import java.util.Date + +data class RecorderNotificationHelper( + val context: Context, + val details: NotificationDetails? = null, +) { + data class NotificationDetails( + val title: String, + val description: String, + val icon: Int, + val isOngoing: Boolean, + ) + + private fun getNotificationChangeStateIntent( + newState: RecorderState, + requestCode: Int + ): PendingIntent { + return PendingIntent.getService( + context, + requestCode, + Intent(context, AudioRecorderService::class.java).apply { + action = "changeState" + putExtra("newState", newState.name) + }, + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE + ) + } + + private fun getIconID(): Int = details?.icon ?: R.drawable.launcher_monochrome_noopacity + + private fun createBaseNotification(): NotificationCompat.Builder { + return NotificationCompat.Builder( + context, + NotificationHelper.RECORDER_CHANNEL_ID + ) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .setSmallIcon(getIconID()) + .setContentIntent( + PendingIntent.getActivity( + context, + 0, + Intent(context, MainActivity::class.java), + PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, + ) + ) + .setSilent(true) + .setOnlyAlertOnce(true) + .setChronometerCountDown(false) + } + + fun buildStartingNotification(): Notification { + return createBaseNotification() + .setContentTitle(context.getString(R.string.ui_audioRecorder_state_recording_title)) + .setContentText(context.getString(R.string.ui_audioRecorder_state_recording_description)) + .build() + } + + fun buildRecordingNotification(recordingTime: Long): Notification { + return createBaseNotification() + .setUsesChronometer(true) + .setOngoing(details?.isOngoing ?: true) + .setShowWhen(true) + .setWhen( + Date.from( + Calendar + .getInstance() + .also { it.add(Calendar.MILLISECOND, -recordingTime.toInt()) } + .toInstant() + ).time, + ) + .addAction( + R.drawable.ic_cancel, + context.getString(R.string.ui_audioRecorder_action_delete_label), + getNotificationChangeStateIntent(RecorderState.IDLE, 1), + ) + .addAction( + R.drawable.ic_pause, + context.getString(R.string.ui_audioRecorder_action_pause_label), + getNotificationChangeStateIntent(RecorderState.PAUSED, 2), + ) + .setContentTitle( + details?.title + ?: context.getString(R.string.ui_audioRecorder_state_recording_title) + ) + .setContentText( + details?.description + ?: context.getString(R.string.ui_audioRecorder_state_recording_description) + ) + .build() + } + + fun buildPausedNotification(start: LocalDateTime): Notification { + return createBaseNotification() + .setContentTitle(context.getString(R.string.ui_audioRecorder_state_paused_title)) + .setContentText(context.getString(R.string.ui_audioRecorder_state_paused_description)) + .setOngoing(false) + .setUsesChronometer(false) + .setWhen(Date.from(start.atZone(ZoneId.systemDefault()).toInstant()).time) + .addAction( + R.drawable.ic_play, + context.getString(R.string.ui_audioRecorder_action_resume_label), + getNotificationChangeStateIntent(RecorderState.RECORDING, 3), + ) + .build() + } +} \ No newline at end of file diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt index 35c7e14..6adfa17 100644 --- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt +++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt @@ -40,7 +40,7 @@ abstract class RecorderService : Service() { private set private lateinit var recordingTimeTimer: ScheduledExecutorService var onRecordingTimeChange: ((Long) -> Unit)? = null - var notificationDetails: NotificationDetails? = null + var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null protected abstract fun start() protected abstract fun pause() @@ -139,7 +139,7 @@ abstract class RecorderService : Service() { fun startRecording() { recordingStart = LocalDateTime.now() - val notification = buildStartNotification() + val notification = getNotificationHelper().buildStartingNotification() startForeground(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, notification) // Start @@ -157,117 +157,26 @@ abstract class RecorderService : Service() { stopSelf() } - private fun buildStartNotification(): Notification = - NotificationCompat.Builder(this, NotificationHelper.RECORDER_CHANNEL_ID) - .setContentTitle(getString(R.string.ui_audioRecorder_state_recording_title)) - .setContentText(getString(R.string.ui_audioRecorder_state_recording_description)) - .setSmallIcon(R.drawable.launcher_foreground) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .build() - - private fun getNotificationChangeStateIntent( - newState: RecorderState, - requestCode: Int - ): PendingIntent { - return PendingIntent.getService( - this, - requestCode, - Intent(this, AudioRecorderService::class.java).apply { - action = "changeState" - putExtra("newState", newState.name) - }, - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE - ) + private fun getNotificationHelper(): RecorderNotificationHelper { + return RecorderNotificationHelper(this, notificationDetails) } - private fun buildNotification(): Notification = when (state) { - RecorderState.RECORDING -> NotificationCompat.Builder( - this, - NotificationHelper.RECORDER_CHANNEL_ID - ) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setWhen( - Date.from( - Calendar - .getInstance() - .also { it.add(Calendar.MILLISECOND, -recordingTime.toInt()) } - .toInstant() - ).time, - ) - .setSilent(true) - .setOnlyAlertOnce(true) - .setUsesChronometer(true) - .setChronometerCountDown(false) - .setContentIntent( - PendingIntent.getActivity( - this, - 0, - Intent(this, MainActivity::class.java), - PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE, - ) - ) - .addAction( - R.drawable.ic_cancel, - getString(R.string.ui_audioRecorder_action_delete_label), - getNotificationChangeStateIntent(RecorderState.IDLE, 1), - ) - .addAction( - R.drawable.ic_pause, - getString(R.string.ui_audioRecorder_action_pause_label), - getNotificationChangeStateIntent(RecorderState.PAUSED, 2), - ) - .apply { - setContentTitle( - notificationDetails?.title - ?: getString(R.string.ui_audioRecorder_state_recording_title) - ) - setContentText( - notificationDetails?.description - ?: getString(R.string.ui_audioRecorder_state_recording_description) - ) - setSmallIcon(notificationDetails?.icon ?: R.drawable.launcher_foreground) - setOngoing(notificationDetails?.isOngoing ?: true) + + private fun buildNotification(): Notification { + val notificationHelper = getNotificationHelper() + + return when (state) { + RecorderState.RECORDING -> { + notificationHelper.buildRecordingNotification(recordingTime) } - .build() - RecorderState.PAUSED -> NotificationCompat.Builder( - this, - NotificationHelper.RECORDER_CHANNEL_ID - ) - .setContentTitle(getString(R.string.ui_audioRecorder_state_paused_title)) - .setContentText(getString(R.string.ui_audioRecorder_state_paused_description)) - .setSmallIcon(R.drawable.launcher_foreground) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setCategory(NotificationCompat.CATEGORY_SERVICE) - .setOngoing(false) - .setOnlyAlertOnce(true) - .setUsesChronometer(false) - .setWhen(Date.from(recordingStart.atZone(ZoneId.systemDefault()).toInstant()).time) - .setShowWhen(true) - .setContentIntent( - PendingIntent.getActivity( - this, - 0, - Intent(this, MainActivity::class.java), - PendingIntent.FLAG_IMMUTABLE, - ) - ) - .addAction( - R.drawable.ic_play, - getString(R.string.ui_audioRecorder_action_resume_label), - getNotificationChangeStateIntent(RecorderState.RECORDING, 3), - ) - .build() + RecorderState.PAUSED -> { + notificationHelper.buildPausedNotification(recordingStart) + } - else -> throw IllegalStateException("Invalid state passed to `buildNotification()`") + else -> { + throw IllegalStateException("Notification can't be built in state $state") + } + } } - - data class NotificationDetails( - val title: String, - val description: String, - val icon: Int, - val isOngoing: Boolean, - ) } \ No newline at end of file