From 01a6f49b77eb9c9ed0db179046fd72d376d529da Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 12:50:27 +0200
Subject: [PATCH 01/24] feat: Add NotificationSettings
---
.../java/app/myzel394/alibi/db/AppSettings.kt | 45 +++++++++++++++++++
app/src/main/res/values/strings.xml | 3 ++
2 files changed, 48 insertions(+)
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
index 2fd5abc..f1fda67 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
@@ -3,6 +3,7 @@ package app.myzel394.alibi.db
import android.media.MediaRecorder
import android.os.Build
import android.util.Log
+import app.myzel394.alibi.R
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import kotlinx.serialization.Serializable
@@ -14,6 +15,7 @@ import java.time.format.DateTimeFormatter.ISO_DATE_TIME
@Serializable
data class AppSettings(
val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings(),
+ val notificationSettings: NotificationSettings = NotificationSettings.fromPreset(NotificationSettings.Preset.Default),
val hasSeenOnboarding: Boolean = false,
val showAdvancedSettings: Boolean = false,
val theme: Theme = Theme.SYSTEM,
@@ -470,3 +472,46 @@ data class AudioRecorderSettings(
}
}
}
+
+@Serializable
+data class NotificationSettings(
+ val title: String,
+ val message: String,
+ val showOngoing: Boolean,
+ val preset: Preset? = null,
+) {
+ @Serializable
+ sealed class Preset(
+ val titleID: Int,
+ val messageID: Int,
+ val showOngoing: Boolean,
+ ) {
+ data object Default : Preset(
+ R.string.ui_audioRecorder_state_recording_title,
+ R.string.ui_audioRecorder_state_recording_description,
+ true,
+ )
+ data object Weather : Preset(
+ R.string.ui_audioRecorder_state_recording_fake_weather_title,
+ R.string.ui_audioRecorder_state_recording_fake_weather_description,
+ false,
+ )
+
+ data object Player : Preset(
+ R.string.ui_audioRecorder_state_recording_fake_weather_title,
+ R.string.ui_audioRecorder_state_recording_fake_weather_description,
+ false,
+ )
+ }
+
+ companion object {
+ fun fromPreset(preset: Preset): NotificationSettings {
+ return NotificationSettings(
+ title = "",
+ message = "",
+ showOngoing = preset.showOngoing,
+ preset = preset,
+ )
+ }
+ }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index baa7240..3ca07f3 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -28,8 +28,11 @@
Alibi will continue recording in the background and store the last %s minutes at your request
Processing
Processing Audio, do not close Alibi! You will be automatically prompted to save the file once it\'s ready
+
Recording Audio
Alibi keeps recording in the background
+ Current Weather
+ 14° with light chance of rain
Welcome to Alibi!
Alibi is like a dashcam for your phone. It allows you to record your audio continuously and save the last 30 minutes when you need it.
From 6ef60942e04c8d517ec738199a7df2aeaf2e20e3 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 16:55:42 +0200
Subject: [PATCH 02/24] fix: Use built in serializer to parse AppSettings
---
.../java/app/myzel394/alibi/db/AppSettings.kt | 70 ++-----------------
1 file changed, 6 insertions(+), 64 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
index f1fda67..cda7f9a 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
@@ -7,6 +7,7 @@ import app.myzel394.alibi.R
import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import kotlinx.serialization.Serializable
+import kotlinx.serialization.json.Json
import org.json.JSONObject
import java.io.File
import java.time.LocalDateTime
@@ -42,45 +43,18 @@ data class AppSettings(
DARK,
}
- fun toJSONObject(): JSONObject {
- return JSONObject(
- mapOf(
- "audioRecorderSettings" to audioRecorderSettings.toJSONObject(),
- "hasSeenOnboarding" to hasSeenOnboarding,
- "showAdvancedSettings" to showAdvancedSettings,
- "theme" to theme.name,
- )
- )
- }
-
fun exportToString(): String {
- return JSONObject(
- mapOf(
- "_meta" to mapOf(
- "version" to 1,
- "date" to LocalDateTime.now().format(ISO_DATE_TIME),
- "app" to "app.myzel394.alibi",
- ),
- "data" to toJSONObject(),
- )
- ).toString(0)
+ return Json.encodeToString(serializer(), this)
}
companion object {
fun getDefaultInstance(): AppSettings = AppSettings()
- fun fromJSONObject(data: JSONObject): AppSettings {
- return AppSettings(
- audioRecorderSettings = AudioRecorderSettings.fromJSONObject(data.getJSONObject("audioRecorderSettings")),
- hasSeenOnboarding = data.getBoolean("hasSeenOnboarding"),
- showAdvancedSettings = data.getBoolean("showAdvancedSettings"),
- theme = Theme.valueOf(data.getString("theme")),
- )
- }
-
fun fromExportedString(data: String): AppSettings {
- val json = JSONObject(data)
- return fromJSONObject(json.getJSONObject("data"))
+ return Json.decodeFromString(
+ serializer(),
+ data,
+ )
}
}
}
@@ -332,20 +306,6 @@ data class AudioRecorderSettings(
return supportedFormats.contains(outputFormat)
}
- fun toJSONObject(): JSONObject {
- return JSONObject(
- mapOf(
- "maxDuration" to maxDuration,
- "intervalDuration" to intervalDuration,
- "forceExactMaxDuration" to forceExactMaxDuration,
- "bitRate" to bitRate,
- "samplingRate" to samplingRate,
- "outputFormat" to outputFormat,
- "encoder" to encoder,
- )
- )
- }
-
companion object {
fun getDefaultInstance(): AudioRecorderSettings = AudioRecorderSettings()
val EXAMPLE_MAX_DURATIONS = listOf(
@@ -452,24 +412,6 @@ data class AudioRecorderSettings(
}
}
}).toMap()
-
- fun fromJSONObject(data: JSONObject): AudioRecorderSettings {
- return AudioRecorderSettings(
- maxDuration = data.getLong("maxDuration"),
- intervalDuration = data.getLong("intervalDuration"),
- forceExactMaxDuration = data.getBoolean("forceExactMaxDuration"),
- bitRate = data.getInt("bitRate"),
- samplingRate = data.optInt("samplingRate", -1).let {
- if (it == -1) null else it
- },
- outputFormat = data.optInt("outputFormat", -1).let {
- if (it == -1) null else it
- },
- encoder = data.optInt("encoder", -1).let {
- if (it == -1) null else it
- },
- )
- }
}
}
From d0d8996227e16ca98125006cc4e4429dba1ddffa Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 18:10:24 +0200
Subject: [PATCH 03/24] feat: Add support for custom notification
---
.../alibi/db/AppSettingsSerializer.kt | 7 +-
.../alibi/services/RecorderService.kt | 70 ++++++++++++++-----
.../alibi/ui/models/AudioRecorderModel.kt | 49 +++++++------
3 files changed, 82 insertions(+), 44 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettingsSerializer.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettingsSerializer.kt
index ca91a75..5f8e60e 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettingsSerializer.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettingsSerializer.kt
@@ -13,7 +13,7 @@ import java.io.InputStream
import java.io.OutputStream
import java.time.LocalDateTime
-class AppSettingsSerializer: Serializer {
+class AppSettingsSerializer : Serializer {
override val defaultValue: AppSettings = AppSettings.getDefaultInstance()
override suspend fun readFrom(input: InputStream): AppSettings {
@@ -39,8 +39,9 @@ class AppSettingsSerializer: Serializer {
}
}
-class LocalDateTimeSerializer: KSerializer {
- override val descriptor: SerialDescriptor = PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
+class LocalDateTimeSerializer : KSerializer {
+ override val descriptor: SerialDescriptor =
+ PrimitiveSerialDescriptor("LocalDateTime", PrimitiveKind.STRING)
override fun deserialize(decoder: Decoder): LocalDateTime {
return LocalDateTime.parse(decoder.decodeString())
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 939f933..35c7e14 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
@@ -23,7 +23,7 @@ import java.util.concurrent.ScheduledExecutorService
import java.util.concurrent.TimeUnit
-abstract class RecorderService: Service() {
+abstract class RecorderService : Service() {
private val binder = RecorderBinder()
private var isPaused: Boolean = false
@@ -40,6 +40,7 @@ abstract class RecorderService: Service() {
private set
private lateinit var recordingTimeTimer: ScheduledExecutorService
var onRecordingTimeChange: ((Long) -> Unit)? = null
+ var notificationDetails: NotificationDetails? = null
protected abstract fun start()
protected abstract fun pause()
@@ -61,7 +62,7 @@ abstract class RecorderService: Service() {
return super.onStartCommand(intent, flags, startId)
}
- inner class RecorderBinder: Binder() {
+ inner class RecorderBinder : Binder() {
fun getService(): RecorderService = this@RecorderService
}
@@ -95,10 +96,12 @@ abstract class RecorderService: Service() {
start()
}
}
+
RecorderState.PAUSED -> {
pause()
isPaused = true
}
+
RecorderState.IDLE -> {
stop()
onDestroy()
@@ -109,6 +112,7 @@ abstract class RecorderService: Service() {
RecorderState.RECORDING -> {
createRecordingTimeTimer()
}
+
RecorderState.PAUSED, RecorderState.IDLE -> {
recordingTimeTimer.shutdown()
}
@@ -121,7 +125,7 @@ abstract class RecorderService: Service() {
RecorderState.PAUSED
).contains(newState) &&
PermissionHelper.hasGranted(this, android.Manifest.permission.POST_NOTIFICATIONS)
- ){
+ ) {
val notification = buildNotification()
NotificationManagerCompat.from(this).notify(
NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID,
@@ -148,19 +152,24 @@ abstract class RecorderService: Service() {
changeState(RecorderState.IDLE)
stopForeground(STOP_FOREGROUND_REMOVE)
- NotificationManagerCompat.from(this).cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID)
+ NotificationManagerCompat.from(this)
+ .cancel(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID)
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 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 {
+ private fun getNotificationChangeStateIntent(
+ newState: RecorderState,
+ requestCode: Int
+ ): PendingIntent {
return PendingIntent.getService(
this,
requestCode,
@@ -172,14 +181,13 @@ abstract class RecorderService: Service() {
)
}
- private fun buildNotification(): Notification = when(state) {
- RecorderState.RECORDING -> 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)
+ private fun buildNotification(): Notification = when (state) {
+ RecorderState.RECORDING -> NotificationCompat.Builder(
+ this,
+ NotificationHelper.RECORDER_CHANNEL_ID
+ )
.setPriority(NotificationCompat.PRIORITY_LOW)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
- .setOngoing(true)
.setWhen(
Date.from(
Calendar
@@ -210,8 +218,24 @@ abstract class RecorderService: Service() {
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)
+ }
.build()
- RecorderState.PAUSED -> NotificationCompat.Builder(this, NotificationHelper.RECORDER_CHANNEL_ID)
+
+ 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)
@@ -236,6 +260,14 @@ abstract class RecorderService: Service() {
getNotificationChangeStateIntent(RecorderState.RECORDING, 3),
)
.build()
+
else -> throw IllegalStateException("Invalid state passed to `buildNotification()`")
}
+
+ data class NotificationDetails(
+ val title: String,
+ val description: String,
+ val icon: Int,
+ val isOngoing: Boolean,
+ )
}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
index e9638cf..3b666cb 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
@@ -18,7 +18,7 @@ import app.myzel394.alibi.enums.RecorderState
import app.myzel394.alibi.services.AudioRecorderService
import app.myzel394.alibi.services.RecorderService
-class AudioRecorderModel: ViewModel() {
+class AudioRecorderModel : ViewModel() {
var recorderState by mutableStateOf(RecorderState.IDLE)
private set
var recordingTime by mutableStateOf(null)
@@ -48,28 +48,29 @@ class AudioRecorderModel: ViewModel() {
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
- recorderService = ((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also {recorder ->
- recorder.onStateChange = { state ->
- recorderState = state
- }
- recorder.onRecordingTimeChange = { time ->
- recordingTime = time
- }
- recorder.onAmplitudeChange = { amps ->
- amplitudes = amps
- onAmplitudeChange()
- }
- recorder.onError = {
- recorderService!!.createLastRecording()
- onError()
- }
- }.also {
- it.startRecording()
+ recorderService =
+ ((service as RecorderService.RecorderBinder).getService() as AudioRecorderService).also { recorder ->
+ recorder.onStateChange = { state ->
+ recorderState = state
+ }
+ recorder.onRecordingTimeChange = { time ->
+ recordingTime = time
+ }
+ recorder.onAmplitudeChange = { amps ->
+ amplitudes = amps
+ onAmplitudeChange()
+ }
+ recorder.onError = {
+ recorderService!!.createLastRecording()
+ onError()
+ }
+ }.also {
+ it.startRecording()
- recorderState = it.state
- recordingTime = it.recordingTime
- amplitudes = it.amplitudes
- }
+ recorderState = it.state
+ recordingTime = it.recordingTime
+ amplitudes = it.amplitudes
+ }
}
override fun onServiceDisconnected(arg0: ComponentName) {
@@ -117,6 +118,10 @@ class AudioRecorderModel: ViewModel() {
recorderService!!.changeState(RecorderState.RECORDING)
}
+ fun setNotificationDetails(details: RecorderService.NotificationDetails) {
+ recorderService?.notificationDetails = details
+ }
+
fun setMaxAmplitudesAmount(amount: Int) {
recorderService?.amplitudesAmount = amount
}
From ac4102d5cd0a8cf4d3cfae2975913f447ccc2250 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 18:22:49 +0200
Subject: [PATCH 04/24] feat: Add CustomNotificationTile.kt to SettingsScreen
---
.../java/app/myzel394/alibi/db/AppSettings.kt | 3 +-
.../atoms/CustomNotificationTile.kt | 53 +++++++++++++++++++
.../alibi/ui/screens/SettingsScreen.kt | 2 +
app/src/main/res/values/strings.xml | 3 ++
4 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
index cda7f9a..be44487 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
@@ -16,7 +16,7 @@ import java.time.format.DateTimeFormatter.ISO_DATE_TIME
@Serializable
data class AppSettings(
val audioRecorderSettings: AudioRecorderSettings = AudioRecorderSettings(),
- val notificationSettings: NotificationSettings = NotificationSettings.fromPreset(NotificationSettings.Preset.Default),
+ val notificationSettings: NotificationSettings? = null,
val hasSeenOnboarding: Boolean = false,
val showAdvancedSettings: Boolean = false,
val theme: Theme = Theme.SYSTEM,
@@ -433,6 +433,7 @@ data class NotificationSettings(
R.string.ui_audioRecorder_state_recording_description,
true,
)
+
data object Weather : Preset(
R.string.ui_audioRecorder_state_recording_fake_weather_title,
R.string.ui_audioRecorder_state_recording_fake_weather_description,
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
new file mode 100644
index 0000000..288d88c
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
@@ -0,0 +1,53 @@
+package app.myzel394.alibi.ui.components.SettingsScreen.atoms
+
+import androidx.compose.foundation.clickable
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ChevronRight
+import androidx.compose.material.icons.filled.Notifications
+import androidx.compose.material3.Icon
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import app.myzel394.alibi.R
+import app.myzel394.alibi.dataStore
+import app.myzel394.alibi.db.AppSettings
+import app.myzel394.alibi.ui.components.atoms.SettingsTile
+
+@Composable
+fun CustomNotificationTile() {
+ val dataStore = LocalContext.current.dataStore
+ val settings = dataStore
+ .data
+ .collectAsState(initial = AppSettings.getDefaultInstance())
+ .value
+
+ val label = if (settings.notificationSettings == null)
+ stringResource(R.string.ui_settings_option_customNotification_description_setup)
+ else stringResource(
+ R.string.ui_settings_option_customNotification_description_edit
+ )
+
+ SettingsTile(
+ firstModifier = Modifier
+ .clickable { }
+ .semantics { contentDescription = label },
+ title = stringResource(R.string.ui_settings_option_customNotification_title),
+ description = label,
+ leading = {
+ Icon(
+ Icons.Default.Notifications,
+ contentDescription = null,
+ )
+ },
+ trailing = {
+ Icon(
+ Icons.Default.ChevronRight,
+ contentDescription = null,
+ )
+ }
+ )
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
index 420c117..5235937 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
@@ -40,6 +40,7 @@ import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ForceExactMaxDurationTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ImportExport
@@ -145,6 +146,7 @@ fun SettingsScreen(
IntervalDurationTile()
ForceExactMaxDurationTile()
InAppLanguagePicker()
+ CustomNotificationTile()
AnimatedVisibility(visible = settings.showAdvancedSettings) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3ca07f3..abe5e8a 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -71,4 +71,7 @@
Are you sure you want to import these settings? Your current settings will be overwritten!
Import settings
Settings have been imported successfully!
+ Custom Notifications
+ Setup custom recording notifications now
+ Edit recording notifications
\ No newline at end of file
From 1e5806000ab16ad4fb617577106f9e4400cc8ccf Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 18:39:38 +0200
Subject: [PATCH 05/24] feat: Add basic CustomRecordingNotificationsScreen
---
.../java/app/myzel394/alibi/ui/Navigation.kt | 20 ++++
.../atoms/CustomNotificationTile.kt | 10 +-
.../app/myzel394/alibi/ui/enums/Screen.kt | 7 +-
.../CustomRecordingNotificationsScreen.kt | 100 ++++++++++++++++++
.../alibi/ui/screens/SettingsScreen.kt | 2 +-
5 files changed, 133 insertions(+), 6 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
index 76a1f7c..e9c0f99 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
@@ -5,6 +5,8 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.animation.scaleIn
import androidx.compose.animation.scaleOut
+import androidx.compose.animation.slideInHorizontally
+import androidx.compose.animation.slideOutHorizontally
import androidx.compose.foundation.background
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
@@ -25,6 +27,7 @@ import app.myzel394.alibi.db.LastRecording
import app.myzel394.alibi.ui.enums.Screen
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.screens.AudioRecorder
+import app.myzel394.alibi.ui.screens.CustomRecordingNotificationsScreen
import app.myzel394.alibi.ui.screens.SettingsScreen
import app.myzel394.alibi.ui.screens.WelcomeScreen
@@ -90,5 +93,22 @@ fun Navigation(
audioRecorder = audioRecorder,
)
}
+ composable(
+ Screen.CustomRecordingNotifications.route,
+ enterTransition = {
+ slideInHorizontally(
+ initialOffsetX = { it -> it / 2 }
+ ) + fadeIn()
+ },
+ exitTransition = {
+ slideOutHorizontally(
+ targetOffsetX = { it -> it / 2 }
+ ) + fadeOut()
+ }
+ ) {
+ CustomRecordingNotificationsScreen(
+ navController = navController,
+ )
+ }
}
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
index 288d88c..76be693 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/SettingsScreen/atoms/CustomNotificationTile.kt
@@ -12,13 +12,17 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.navigation.NavController
import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.components.atoms.SettingsTile
+import app.myzel394.alibi.ui.enums.Screen
@Composable
-fun CustomNotificationTile() {
+fun CustomNotificationTile(
+ navController: NavController,
+) {
val dataStore = LocalContext.current.dataStore
val settings = dataStore
.data
@@ -33,7 +37,9 @@ fun CustomNotificationTile() {
SettingsTile(
firstModifier = Modifier
- .clickable { }
+ .clickable {
+ navController.navigate(Screen.CustomRecordingNotifications.route)
+ }
.semantics { contentDescription = label },
title = stringResource(R.string.ui_settings_option_customNotification_title),
description = label,
diff --git a/app/src/main/java/app/myzel394/alibi/ui/enums/Screen.kt b/app/src/main/java/app/myzel394/alibi/ui/enums/Screen.kt
index 136d7b8..218bde3 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/enums/Screen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/enums/Screen.kt
@@ -1,9 +1,10 @@
package app.myzel394.alibi.ui.enums
sealed class Screen(val route: String) {
- object AudioRecorder : Screen("audio-recorder")
- object Settings : Screen("settings")
- object Welcome : Screen("welcome")
+ data object AudioRecorder : Screen("audio-recorder")
+ data object Settings : Screen("settings")
+ data object Welcome : Screen("welcome")
+ data object CustomRecordingNotifications : Screen("custom-recording-notifications")
fun withArgs(vararg args: String): String {
return buildString {
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
new file mode 100644
index 0000000..26396c8
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -0,0 +1,100 @@
+package app.myzel394.alibi.ui.screens
+
+import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowBack
+import androidx.compose.material3.Divider
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.LargeTopAppBar
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.Snackbar
+import androidx.compose.material3.SnackbarHost
+import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBarDefaults
+import androidx.compose.material3.rememberTopAppBarState
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import androidx.navigation.NavController
+import app.myzel394.alibi.R
+import app.myzel394.alibi.dataStore
+import app.myzel394.alibi.db.AppSettings
+import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ForceExactMaxDurationTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ImportExport
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.IntervalDurationTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.MaxDurationTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.OutputFormatTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SamplingRateTile
+import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector
+import app.myzel394.alibi.ui.components.atoms.GlobalSwitch
+import app.myzel394.alibi.ui.components.atoms.MessageBox
+import app.myzel394.alibi.ui.components.atoms.MessageType
+import kotlinx.coroutines.launch
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun CustomRecordingNotificationsScreen(
+ navController: NavController,
+) {
+ val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
+ rememberTopAppBarState()
+ )
+
+ val dataStore = LocalContext.current.dataStore
+ val settings = dataStore
+ .data
+ .collectAsState(initial = AppSettings.getDefaultInstance())
+ .value
+
+ Scaffold(
+ topBar = {
+ LargeTopAppBar(
+ title = {
+ Text(stringResource(R.string.ui_settings_option_customNotification_title))
+ },
+ navigationIcon = {
+ IconButton(onClick = navController::popBackStack) {
+ Icon(
+ Icons.Default.ArrowBack,
+ contentDescription = "Back"
+ )
+ }
+ },
+ scrollBehavior = scrollBehavior,
+ )
+ },
+ modifier = Modifier
+ .nestedScroll(scrollBehavior.nestedScrollConnection)
+ ) { padding ->
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(padding)
+ .verticalScroll(rememberScrollState()),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
index 5235937..b64a964 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/SettingsScreen.kt
@@ -146,7 +146,7 @@ fun SettingsScreen(
IntervalDurationTile()
ForceExactMaxDurationTile()
InAppLanguagePicker()
- CustomNotificationTile()
+ CustomNotificationTile(navController = navController)
AnimatedVisibility(visible = settings.showAdvancedSettings) {
Column(
horizontalAlignment = Alignment.CenterHorizontally,
From 17a52fdcb5d2c89bc6842c9c1eca4229d31077ee Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 19:17:42 +0200
Subject: [PATCH 06/24] feat: Add LandingElement for
CustomRecordingNotificationsScreen
---
.../atoms/LandingElement.kt | 111 ++++++++++++++++++
.../CustomRecordingNotificationsScreen.kt | 16 +--
.../alibi/ui/utils/PermissionHelper.kt | 15 +++
...ic_custom_recording_notifications_blob.xml | 13 ++
app/src/main/res/values/strings.xml | 4 +
5 files changed, 151 insertions(+), 8 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
create mode 100644 app/src/main/res/drawable/ic_custom_recording_notifications_blob.xml
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
new file mode 100644
index 0000000..08cebaa
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
@@ -0,0 +1,111 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.ArrowRightAlt
+import androidx.compose.material.icons.filled.Edit
+import androidx.compose.material.icons.filled.Notifications
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.R
+import app.myzel394.alibi.ui.utils.openNotificationsSettings
+
+@Composable
+fun LandingElement(
+ modifier: Modifier = Modifier,
+) {
+ val context = LocalContext.current
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 32.dp)
+ .then(modifier),
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.SpaceBetween,
+ ) {
+ Box() {}
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ ) {
+ Box(
+ contentAlignment = Alignment.Center,
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.ic_custom_recording_notifications_blob),
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.tertiaryContainer),
+ contentDescription = null,
+ modifier = Modifier
+ .width(512.dp)
+ )
+ Icon(
+ imageVector = Icons.Default.Notifications,
+ contentDescription = null,
+ tint = MaterialTheme.colorScheme.tertiary,
+ modifier = Modifier
+ .size(128.dp)
+ )
+ }
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Text(
+ stringResource(R.string.ui_settings_customNotifications_landing_title),
+ style = MaterialTheme.typography.headlineMedium,
+ )
+ Text(
+ stringResource(R.string.ui_settings_customNotifications_landing_description),
+ style = MaterialTheme.typography.bodySmall,
+ )
+ Button(
+ onClick = {},
+ colors = ButtonDefaults.filledTonalButtonColors(),
+ ) {
+ Icon(
+ Icons.Default.Edit,
+ contentDescription = null,
+ modifier = Modifier.size(ButtonDefaults.IconSize)
+ )
+ Spacer(modifier = Modifier.width(ButtonDefaults.IconSpacing))
+ Text(
+ stringResource(
+ R.string.ui_settings_customNotifications_landing_getStarted
+ )
+ )
+ }
+ }
+ }
+ Button(
+ onClick = context::openNotificationsSettings,
+ colors = ButtonDefaults.textButtonColors(),
+ ) {
+ Text(
+ stringResource(R.string.ui_settings_customNotifications_landing_help_hideNotifications),
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index 26396c8..908b4db 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -21,6 +21,7 @@ import androidx.compose.material3.Scaffold
import androidx.compose.material3.Snackbar
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
+import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
@@ -37,6 +38,7 @@ import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.LandingElement
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile
@@ -70,7 +72,7 @@ fun CustomRecordingNotificationsScreen(
Scaffold(
topBar = {
- LargeTopAppBar(
+ TopAppBar(
title = {
Text(stringResource(R.string.ui_settings_option_customNotification_title))
},
@@ -88,13 +90,11 @@ fun CustomRecordingNotificationsScreen(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
) { padding ->
- Column(
- modifier = Modifier
- .fillMaxSize()
- .padding(padding)
- .verticalScroll(rememberScrollState()),
- horizontalAlignment = Alignment.CenterHorizontally,
- ) {
+ if (settings.notificationSettings == null) {
+ LandingElement(
+ modifier = Modifier
+ .padding(padding),
+ )
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/utils/PermissionHelper.kt b/app/src/main/java/app/myzel394/alibi/ui/utils/PermissionHelper.kt
index 8d42b6b..f6038e9 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/utils/PermissionHelper.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/utils/PermissionHelper.kt
@@ -5,6 +5,7 @@ import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
+import android.os.Build
import android.provider.Settings
import androidx.core.app.ActivityCompat
@@ -32,3 +33,17 @@ fun Context.openAppSystemSettings() {
data = Uri.fromParts("package", packageName, null)
})
}
+
+fun Context.openNotificationsSettings() {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ startActivity(Intent().apply {
+ action = Settings.ACTION_APP_NOTIFICATION_SETTINGS
+ putExtra(Settings.EXTRA_APP_PACKAGE, packageName)
+ })
+ } else {
+ startActivity(Intent().apply {
+ action = Settings.ACTION_APPLICATION_DETAILS_SETTINGS
+ data = Uri.fromParts("package", packageName, null)
+ })
+ }
+}
diff --git a/app/src/main/res/drawable/ic_custom_recording_notifications_blob.xml b/app/src/main/res/drawable/ic_custom_recording_notifications_blob.xml
new file mode 100644
index 0000000..6bb10ef
--- /dev/null
+++ b/app/src/main/res/drawable/ic_custom_recording_notifications_blob.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index abe5e8a..8397559 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -74,4 +74,8 @@
Custom Notifications
Setup custom recording notifications now
Edit recording notifications
+ Don\'t expose yourself
+ Due to Android\'s restrictions, Alibi has to show a notification while recording. To hide the fact that you\'re using Alibi, you can customize the notification.
+ Alternatively, you can also simply disable notifications
+ Create own notification
\ No newline at end of file
From 119782fb8f2a2d5e6bab480347ede8e24d6ba8d6 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 20:51:50 +0200
Subject: [PATCH 07/24] feat: Adding EditNotificationInput
---
.../atoms/EditNotificationInput.kt | 181 ++++++++++++++++++
.../myzel394/alibi/ui/effects/force-update.kt | 23 +++
.../CustomRecordingNotificationsScreen.kt | 19 +-
3 files changed, 221 insertions(+), 2 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/effects/force-update.kt
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt
new file mode 100644
index 0000000..1ae7f19
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt
@@ -0,0 +1,181 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
+
+import androidx.compose.animation.core.animateFloatAsState
+import androidx.compose.animation.core.tween
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.foundation.shape.RoundedCornerShape
+import androidx.compose.foundation.text.BasicTextField
+import androidx.compose.foundation.text.KeyboardOptions
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Circle
+import androidx.compose.material3.Icon
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.material3.TextField
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.alpha
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.Color
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.toArgb
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.text.input.ImeAction
+import androidx.compose.ui.text.input.KeyboardType
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.R
+import app.myzel394.alibi.ui.effects.rememberForceUpdate
+import com.maxkeppeler.sheets.input.models.InputText
+import java.time.Duration
+import java.time.LocalDateTime
+import java.time.Period
+
+@Composable
+fun EditNotificationInput(
+ modifier: Modifier = Modifier,
+ showOngoing: Boolean,
+ title: String,
+ description: String,
+ onShowOngoingChange: (Boolean) -> Unit,
+ onTitleChange: (String) -> Unit,
+ onDescriptionChange: (String) -> Unit,
+) {
+ var ongoingStartTime by remember { mutableStateOf(LocalDateTime.now()) }
+
+ val secondaryColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
+
+ Row(
+ modifier = Modifier
+ .clip(MaterialTheme.shapes.medium)
+ .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.6f))
+ .padding(16.dp)
+ .then(modifier),
+ verticalAlignment = Alignment.Top,
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ val headlineSize = 22.dp
+
+ Box(
+ modifier = Modifier
+ .size(headlineSize)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.secondary)
+ .padding(1.dp),
+ ) {
+ Image(
+ painter = painterResource(id = R.drawable.launcher_foreground),
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimary),
+ )
+ }
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(6.dp),
+ modifier = Modifier.height(headlineSize),
+ ) {
+ Text(
+ stringResource(R.string.app_name),
+ style = MaterialTheme.typography.bodySmall,
+ color = secondaryColor,
+ )
+ if (showOngoing) {
+ Icon(
+ Icons.Default.Circle,
+ contentDescription = null,
+ tint = secondaryColor,
+ modifier = Modifier
+ .size(8.dp)
+ )
+
+ val fakeAlpha = rememberForceUpdate()
+ val formattedTime = {
+ val difference =
+ Duration.between(
+ ongoingStartTime,
+ LocalDateTime.now(),
+ )
+ val minutes = difference.toMinutes()
+ val seconds = difference.minusMinutes(minutes).seconds
+
+ "${if (minutes < 10) "0$minutes" else minutes}:${if (seconds < 10) "0$seconds" else seconds}"
+ }
+ Text(
+ formattedTime(),
+ modifier = Modifier.alpha(fakeAlpha),
+ style = MaterialTheme.typography.bodySmall,
+ color = secondaryColor,
+ )
+ }
+ }
+ Column(
+ verticalArrangement = Arrangement.spacedBy(6.dp),
+ ) {
+ BasicTextField(
+ value = title,
+ onValueChange = onTitleChange,
+ textStyle = MaterialTheme.typography.titleMedium.copy(
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ ),
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Next,
+ ),
+ )
+ BasicTextField(
+ value = description,
+ onValueChange = onDescriptionChange,
+ textStyle = MaterialTheme.typography.bodyMedium.copy(
+ fontWeight = FontWeight.Bold,
+ color = MaterialTheme.colorScheme.onSurfaceVariant,
+ ),
+ cursorBrush = SolidColor(MaterialTheme.colorScheme.onSurfaceVariant),
+ singleLine = true,
+ keyboardOptions = KeyboardOptions(
+ keyboardType = KeyboardType.Text,
+ imeAction = ImeAction.Done,
+ ),
+ )
+ }
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ ) {
+ Text(
+ stringResource(R.string.ui_audioRecorder_action_delete_label),
+ color = MaterialTheme.colorScheme.secondary,
+ fontSize = MaterialTheme.typography.bodyMedium.fontSize,
+ fontWeight = FontWeight.Bold,
+ )
+ Text(
+ stringResource(R.string.ui_audioRecorder_action_pause_label),
+ color = MaterialTheme.colorScheme.secondary,
+ fontSize = MaterialTheme.typography.bodyMedium.fontSize,
+ fontWeight = FontWeight.Bold,
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/effects/force-update.kt b/app/src/main/java/app/myzel394/alibi/ui/effects/force-update.kt
new file mode 100644
index 0000000..be81a62
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/effects/force-update.kt
@@ -0,0 +1,23 @@
+package app.myzel394.alibi.ui.effects
+
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import kotlinx.coroutines.delay
+
+@Composable
+fun rememberForceUpdate(
+ time: Long = 100L,
+): Float {
+ var tickTack by rememberSaveable { mutableStateOf(1f) }
+
+ LaunchedEffect(tickTack) {
+ delay(time)
+ tickTack = if (tickTack == 1f) 0.99f else 1f
+ }
+
+ return tickTack
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index 908b4db..3dd8bcd 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -38,6 +38,7 @@ import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.EditNotificationInput
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.LandingElement
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile
import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile
@@ -91,10 +92,24 @@ fun CustomRecordingNotificationsScreen(
.nestedScroll(scrollBehavior.nestedScrollConnection)
) { padding ->
if (settings.notificationSettings == null) {
- LandingElement(
+ }
+ Box(
+ modifier = Modifier
+ .padding(padding)
+ .padding(vertical = 64.dp)
+ ) {
+ EditNotificationInput(
modifier = Modifier
- .padding(padding),
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ showOngoing = true,
+ title = "Alibi",
+ description = "test",
+ onShowOngoingChange = {},
+ onTitleChange = {},
+ onDescriptionChange = {},
)
+
}
}
}
\ No newline at end of file
From dd57ce513e1adb99a6d4223aff25fdb003591b90 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 21:29:07 +0200
Subject: [PATCH 08/24] feat: Add NotificationPresetSelect; Add more presets
---
.../java/app/myzel394/alibi/db/AppSettings.kt | 31 +++++++++-
.../atoms/NotificationPresetSelect.kt | 61 +++++++++++++++++++
.../atoms/PreviewIcon.kt | 42 +++++++++++++
.../EditNotificationInput.kt | 21 +++----
.../CustomRecordingNotificationsScreen.kt | 54 ++++++++--------
app/src/main/res/drawable/ic_cloud.xml | 5 ++
app/src/main/res/drawable/ic_download.xml | 5 ++
app/src/main/res/drawable/ic_note.xml | 5 ++
app/src/main/res/drawable/ic_vpn.xml | 5 ++
.../launcher_monochrome_noopacity.xml | 10 +++
app/src/main/res/values/strings.xml | 6 ++
11 files changed, 198 insertions(+), 47 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
rename app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/{atoms => molecules}/EditNotificationInput.kt (93%)
create mode 100644 app/src/main/res/drawable/ic_cloud.xml
create mode 100644 app/src/main/res/drawable/ic_download.xml
create mode 100644 app/src/main/res/drawable/ic_note.xml
create mode 100644 app/src/main/res/drawable/ic_vpn.xml
create mode 100644 app/src/main/res/drawable/launcher_monochrome_noopacity.xml
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
index be44487..a91b4ab 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
@@ -8,7 +8,6 @@ import com.arthenica.ffmpegkit.FFmpegKit
import com.arthenica.ffmpegkit.ReturnCode
import kotlinx.serialization.Serializable
import kotlinx.serialization.json.Json
-import org.json.JSONObject
import java.io.File
import java.time.LocalDateTime
import java.time.format.DateTimeFormatter.ISO_DATE_TIME
@@ -427,23 +426,41 @@ data class NotificationSettings(
val titleID: Int,
val messageID: Int,
val showOngoing: Boolean,
+ val iconID: Int,
) {
data object Default : Preset(
R.string.ui_audioRecorder_state_recording_title,
R.string.ui_audioRecorder_state_recording_description,
true,
+ R.drawable.launcher_monochrome_noopacity,
)
data object Weather : Preset(
R.string.ui_audioRecorder_state_recording_fake_weather_title,
R.string.ui_audioRecorder_state_recording_fake_weather_description,
false,
+ R.drawable.ic_cloud
)
data object Player : Preset(
- R.string.ui_audioRecorder_state_recording_fake_weather_title,
- R.string.ui_audioRecorder_state_recording_fake_weather_description,
+ R.string.ui_audioRecorder_state_recording_fake_player_title,
+ R.string.ui_audioRecorder_state_recording_fake_player_description,
+ true,
+ R.drawable.ic_note,
+ )
+
+ data object Browser : Preset(
+ R.string.ui_audioRecorder_state_recording_fake_browser_title,
+ R.string.ui_audioRecorder_state_recording_fake_browser_description,
+ true,
+ R.drawable.ic_download,
+ )
+
+ data object VPN : Preset(
+ R.string.ui_audioRecorder_state_recording_fake_vpn_title,
+ R.string.ui_audioRecorder_state_recording_fake_vpn_description,
false,
+ R.drawable.ic_vpn,
)
}
@@ -456,5 +473,13 @@ data class NotificationSettings(
preset = preset,
)
}
+
+ val PRESETS = listOf(
+ Preset.Default,
+ Preset.Weather,
+ Preset.Player,
+ Preset.Browser,
+ Preset.VPN,
+ )
}
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
new file mode 100644
index 0000000..f90bbe2
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
@@ -0,0 +1,61 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
+
+import androidx.compose.foundation.background
+import androidx.compose.foundation.border
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.text.font.FontWeight
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.db.NotificationSettings
+
+@Composable
+fun NotificationPresetSelect(
+ modifier: Modifier = Modifier,
+ preset: NotificationSettings.Preset
+) {
+ Row(
+ verticalAlignment = Alignment.CenterVertically,
+ horizontalArrangement = Arrangement.spacedBy(16.dp),
+ modifier = Modifier
+ .clip(MaterialTheme.shapes.large)
+ .background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f))
+ .border(
+ width = 1.dp,
+ shape = MaterialTheme.shapes.large,
+ color = MaterialTheme.colorScheme.outline.copy(alpha = 0.4f)
+ )
+ .padding(horizontal = 16.dp, vertical = 8.dp)
+ .then(modifier),
+ ) {
+ PreviewIcon(
+ modifier = Modifier.size(32.dp),
+ painter = painterResource(id = preset.iconID),
+ )
+ Column(
+ horizontalAlignment = Alignment.Start,
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ Text(
+ text = stringResource(preset.titleID),
+ style = MaterialTheme.typography.titleMedium,
+ fontWeight = FontWeight.Bold,
+ )
+ Text(
+ text = stringResource(preset.messageID),
+ style = MaterialTheme.typography.bodyMedium,
+ fontWeight = FontWeight.Normal,
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
new file mode 100644
index 0000000..89b02dc
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
@@ -0,0 +1,42 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
+
+import androidx.compose.foundation.Image
+import androidx.compose.foundation.background
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.shape.CircleShape
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
+import androidx.compose.ui.graphics.ColorFilter
+import androidx.compose.ui.graphics.painter.Painter
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.R
+
+@Composable
+fun PreviewIcon(
+ modifier: Modifier = Modifier,
+ painter: Painter,
+) {
+ Column(
+ horizontalAlignment = Alignment.CenterHorizontally,
+ verticalArrangement = Arrangement.Center,
+ modifier = Modifier
+ .then(modifier)
+ .clip(CircleShape)
+ .background(MaterialTheme.colorScheme.secondary)
+ .padding(1.dp)
+ ) {
+ Image(
+ painter = painter,
+ contentDescription = null,
+ colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimary),
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
similarity index 93%
rename from app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt
rename to app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
index 1ae7f19..73e2960 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/EditNotificationInput.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
@@ -1,4 +1,4 @@
-package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.tween
@@ -41,6 +41,7 @@ import androidx.compose.ui.text.input.ImeAction
import androidx.compose.ui.text.input.KeyboardType
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.PreviewIcon
import app.myzel394.alibi.ui.effects.rememberForceUpdate
import com.maxkeppeler.sheets.input.models.InputText
import java.time.Duration
@@ -72,19 +73,11 @@ fun EditNotificationInput(
) {
val headlineSize = 22.dp
- Box(
- modifier = Modifier
- .size(headlineSize)
- .clip(CircleShape)
- .background(MaterialTheme.colorScheme.secondary)
- .padding(1.dp),
- ) {
- Image(
- painter = painterResource(id = R.drawable.launcher_foreground),
- contentDescription = null,
- colorFilter = ColorFilter.tint(MaterialTheme.colorScheme.onPrimary),
- )
- }
+ PreviewIcon(
+ modifier = Modifier.size(headlineSize),
+ painter = painterResource(id = R.drawable.launcher_foreground)
+ )
+
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(16.dp),
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index 3dd8bcd..a2cc7a2 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -1,33 +1,25 @@
package app.myzel394.alibi.ui.screens
-import androidx.compose.animation.AnimatedVisibility
+import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.rememberScrollState
-import androidx.compose.foundation.verticalScroll
+import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material3.Divider
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
-import androidx.compose.material3.LargeTopAppBar
-import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
-import androidx.compose.material3.Snackbar
-import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.Text
import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
-import androidx.compose.runtime.rememberCoroutineScope
-import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -37,24 +29,9 @@ import androidx.navigation.NavController
import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
-import app.myzel394.alibi.ui.SUPPORTS_DARK_MODE_NATIVELY
-import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.EditNotificationInput
-import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.LandingElement
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.BitrateTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.CustomNotificationTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.EncoderTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ForceExactMaxDurationTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ImportExport
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.InAppLanguagePicker
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.IntervalDurationTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.MaxDurationTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.OutputFormatTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.SamplingRateTile
-import app.myzel394.alibi.ui.components.SettingsScreen.atoms.ThemeSelector
-import app.myzel394.alibi.ui.components.atoms.GlobalSwitch
-import app.myzel394.alibi.ui.components.atoms.MessageBox
-import app.myzel394.alibi.ui.components.atoms.MessageType
-import kotlinx.coroutines.launch
+import app.myzel394.alibi.db.NotificationSettings
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -93,10 +70,12 @@ fun CustomRecordingNotificationsScreen(
) { padding ->
if (settings.notificationSettings == null) {
}
- Box(
+ Column(
modifier = Modifier
+ .fillMaxSize()
.padding(padding)
- .padding(vertical = 64.dp)
+ .padding(vertical = 64.dp, horizontal = 16.dp),
+ verticalArrangement = Arrangement.SpaceBetween,
) {
EditNotificationInput(
modifier = Modifier
@@ -109,7 +88,22 @@ fun CustomRecordingNotificationsScreen(
onTitleChange = {},
onDescriptionChange = {},
)
+ LazyColumn(
+ verticalArrangement = Arrangement.spacedBy(8.dp),
+ ) {
+ items(NotificationSettings.PRESETS.size) {
+ val preset = NotificationSettings.PRESETS[it]
+ NotificationPresetSelect(
+ modifier = Modifier
+ .fillMaxWidth()
+ .clickable {
+
+ },
+ preset = preset,
+ )
+ }
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_cloud.xml b/app/src/main/res/drawable/ic_cloud.xml
new file mode 100644
index 0000000..a860632
--- /dev/null
+++ b/app/src/main/res/drawable/ic_cloud.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_download.xml b/app/src/main/res/drawable/ic_download.xml
new file mode 100644
index 0000000..987f215
--- /dev/null
+++ b/app/src/main/res/drawable/ic_download.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_note.xml b/app/src/main/res/drawable/ic_note.xml
new file mode 100644
index 0000000..4bd8b20
--- /dev/null
+++ b/app/src/main/res/drawable/ic_note.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_vpn.xml b/app/src/main/res/drawable/ic_vpn.xml
new file mode 100644
index 0000000..1339fb3
--- /dev/null
+++ b/app/src/main/res/drawable/ic_vpn.xml
@@ -0,0 +1,5 @@
+
+
+
diff --git a/app/src/main/res/drawable/launcher_monochrome_noopacity.xml b/app/src/main/res/drawable/launcher_monochrome_noopacity.xml
new file mode 100644
index 0000000..6bded29
--- /dev/null
+++ b/app/src/main/res/drawable/launcher_monochrome_noopacity.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8397559..2dbf736 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -78,4 +78,10 @@
Due to Android\'s restrictions, Alibi has to show a notification while recording. To hide the fact that you\'re using Alibi, you can customize the notification.
Alternatively, you can also simply disable notifications
Create own notification
+ Playing Audio
+ Now playing: Despacito
+ Downloading attachments.zip
+ Downloading file...
+ Connected to VPN
+ Connection Secured
\ No newline at end of file
From 5a55619e55f47c192ea61d06809b384d28be63d4 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 21:46:41 +0200
Subject: [PATCH 09/24] feat: Make presets workable
---
.../atoms/NotificationPresetSelect.kt | 4 +-
.../atoms/PreviewIcon.kt | 2 +-
.../molecules/EditNotificationInput.kt | 5 +-
.../organisms/NotificationEditor.kt | 110 ++++++++++++++++++
.../CustomRecordingNotificationsScreen.kt | 43 ++-----
app/src/main/res/values/strings.xml | 1 +
6 files changed, 127 insertions(+), 38 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
index f90bbe2..3df2775 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/NotificationPresetSelect.kt
@@ -29,14 +29,14 @@ fun NotificationPresetSelect(
horizontalArrangement = Arrangement.spacedBy(16.dp),
modifier = Modifier
.clip(MaterialTheme.shapes.large)
+ .then(modifier)
.background(MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f))
.border(
width = 1.dp,
shape = MaterialTheme.shapes.large,
color = MaterialTheme.colorScheme.outline.copy(alpha = 0.4f)
)
- .padding(horizontal = 16.dp, vertical = 8.dp)
- .then(modifier),
+ .padding(horizontal = 16.dp, vertical = 8.dp),
) {
PreviewIcon(
modifier = Modifier.size(32.dp),
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
index 89b02dc..1e3c557 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/PreviewIcon.kt
@@ -31,7 +31,7 @@ fun PreviewIcon(
.then(modifier)
.clip(CircleShape)
.background(MaterialTheme.colorScheme.secondary)
- .padding(1.dp)
+ .padding(2.dp)
) {
Image(
painter = painter,
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
index 73e2960..1aab0de 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
@@ -33,6 +33,7 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.SolidColor
+import androidx.compose.ui.graphics.painter.Painter
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@@ -54,9 +55,11 @@ fun EditNotificationInput(
showOngoing: Boolean,
title: String,
description: String,
+ icon: Painter,
onShowOngoingChange: (Boolean) -> Unit,
onTitleChange: (String) -> Unit,
onDescriptionChange: (String) -> Unit,
+ onIconChange: (Int) -> Unit,
) {
var ongoingStartTime by remember { mutableStateOf(LocalDateTime.now()) }
@@ -75,7 +78,7 @@ fun EditNotificationInput(
PreviewIcon(
modifier = Modifier.size(headlineSize),
- painter = painterResource(id = R.drawable.launcher_foreground)
+ painter = icon,
)
Column(
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
new file mode 100644
index 0000000..685430c
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -0,0 +1,110 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms
+
+import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.scrollable
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.TopAppBarScrollBehavior
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.R
+import app.myzel394.alibi.db.NotificationSettings
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
+
+@OptIn(ExperimentalMaterial3Api::class)
+@Composable
+fun NotificationEditor(
+ modifier: Modifier = Modifier,
+ scrollState: ScrollState,
+) {
+ val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title)
+ val defaultDescription = stringResource(R.string.ui_audioRecorder_state_recording_description)
+
+ var title: String by rememberSaveable {
+ mutableStateOf(defaultTitle)
+ }
+ var description: String by rememberSaveable {
+ mutableStateOf(defaultDescription)
+ }
+ var showOngoing: Boolean by rememberSaveable {
+ mutableStateOf(true)
+ }
+ var icon: Int by rememberSaveable {
+ mutableStateOf(R.drawable.launcher_monochrome_noopacity)
+ }
+
+ Column(
+ modifier = Modifier
+ .fillMaxSize()
+ .padding(horizontal = 16.dp)
+ .then(modifier),
+ verticalArrangement = Arrangement.SpaceBetween,
+ ) {
+ EditNotificationInput(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ showOngoing = true,
+ title = title,
+ description = description,
+ icon = painterResource(icon),
+ onShowOngoingChange = {
+ showOngoing = it
+ },
+ onTitleChange = {
+ title = it
+ },
+ onDescriptionChange = {
+ description = it
+ },
+ onIconChange = {
+ icon = it
+ },
+ )
+
+ Column(
+ verticalArrangement = Arrangement.spacedBy(4.dp),
+ ) {
+ for (preset in NotificationSettings.PRESETS) {
+ val label = stringResource(
+ R.string.ui_settings_customNotifications_preset_apply_label,
+ stringResource(preset.titleID)
+ )
+ val presetTitle = stringResource(preset.titleID)
+ val presetDescription = stringResource(preset.messageID)
+
+ NotificationPresetSelect(
+ modifier = Modifier
+ .fillMaxWidth()
+ .semantics {
+ contentDescription = label
+ }
+ .clickable {
+ title = presetTitle
+ description = presetDescription
+ icon = preset.iconID
+ showOngoing = preset.showOngoing
+ },
+ preset = preset,
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index a2cc7a2..7add6e2 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -1,13 +1,15 @@
package app.myzel394.alibi.ui.screens
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
@@ -32,6 +34,7 @@ import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.NotificationSettings
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms.NotificationEditor
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -41,6 +44,7 @@ fun CustomRecordingNotificationsScreen(
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberTopAppBarState()
)
+ val scrollState = rememberScrollState()
val dataStore = LocalContext.current.dataStore
val settings = dataStore
@@ -70,40 +74,11 @@ fun CustomRecordingNotificationsScreen(
) { padding ->
if (settings.notificationSettings == null) {
}
- Column(
+ NotificationEditor(
modifier = Modifier
- .fillMaxSize()
.padding(padding)
- .padding(vertical = 64.dp, horizontal = 16.dp),
- verticalArrangement = Arrangement.SpaceBetween,
- ) {
- EditNotificationInput(
- modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- showOngoing = true,
- title = "Alibi",
- description = "test",
- onShowOngoingChange = {},
- onTitleChange = {},
- onDescriptionChange = {},
- )
- LazyColumn(
- verticalArrangement = Arrangement.spacedBy(8.dp),
- ) {
- items(NotificationSettings.PRESETS.size) {
- val preset = NotificationSettings.PRESETS[it]
-
- NotificationPresetSelect(
- modifier = Modifier
- .fillMaxWidth()
- .clickable {
-
- },
- preset = preset,
- )
- }
- }
- }
+ .verticalScroll(scrollState),
+ scrollState = scrollState,
+ )
}
}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2dbf736..8b75438 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -84,4 +84,5 @@
Downloading file...
Connected to VPN
Connection Secured
+ Apply Preset \"%s\"
\ No newline at end of file
From 54ad067cdf4052ac0fc8c2399a1571caa7f9d3b7 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Mon, 23 Oct 2023 21:58:40 +0200
Subject: [PATCH 10/24] feat: Add showOngoing button
---
.../molecules/EditNotificationInput.kt | 7 +++
.../organisms/NotificationEditor.kt | 47 +++++++++++++++++--
app/src/main/res/values/strings.xml | 1 +
3 files changed, 50 insertions(+), 5 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
index 1aab0de..c3dcc54 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/EditNotificationInput.kt
@@ -22,6 +22,7 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.TextField
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
@@ -65,6 +66,12 @@ fun EditNotificationInput(
val secondaryColor = MaterialTheme.colorScheme.onSurface.copy(alpha = 0.6f)
+ LaunchedEffect(showOngoing) {
+ if (showOngoing) {
+ ongoingStartTime = LocalDateTime.now()
+ }
+ }
+
Row(
modifier = Modifier
.clip(MaterialTheme.shapes.medium)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index 685430c..ee1e921 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -1,27 +1,33 @@
package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms
import androidx.compose.foundation.ScrollState
+import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.gestures.scrollable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
-import androidx.compose.foundation.lazy.LazyColumn
-import androidx.compose.foundation.verticalScroll
+import androidx.compose.material3.Checkbox
+import androidx.compose.material3.CheckboxColors
+import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
-import androidx.compose.material3.TopAppBarScrollBehavior
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
+import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.db.NotificationSettings
@@ -50,6 +56,7 @@ fun NotificationEditor(
mutableStateOf(R.drawable.launcher_monochrome_noopacity)
}
+ // TODO: Add Preview functionality
Column(
modifier = Modifier
.fillMaxSize()
@@ -61,7 +68,7 @@ fun NotificationEditor(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
- showOngoing = true,
+ showOngoing = showOngoing,
title = title,
description = description,
icon = painterResource(icon),
@@ -79,6 +86,36 @@ fun NotificationEditor(
},
)
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxSize()
+ .clip(MaterialTheme.shapes.medium)
+ .clickable {
+ showOngoing = showOngoing.not()
+ }
+ .background(MaterialTheme.colorScheme.tertiaryContainer)
+ .padding(8.dp),
+ ) {
+ Checkbox(
+ checked = showOngoing,
+ onCheckedChange = {
+ showOngoing = it
+ },
+ colors = CheckboxDefaults.colors(
+ checkedColor = MaterialTheme.colorScheme.tertiary,
+ checkmarkColor = MaterialTheme.colorScheme.onTertiary,
+ )
+ )
+ Text(
+ text = stringResource(R.string.ui_settings_customNotifications_showOngoing_label),
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onTertiaryContainer,
+ fontWeight = FontWeight.Bold,
+ )
+ }
+
Column(
verticalArrangement = Arrangement.spacedBy(4.dp),
) {
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 8b75438..3e5c185 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -85,4 +85,5 @@
Connected to VPN
Connection Secured
Apply Preset \"%s\"
+ Show Duration
\ No newline at end of file
From d0885ba877fef0b76d58b0f8e1ea5a9e8730da72 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 10:10:40 +0200
Subject: [PATCH 11/24] feat: Improve CustomRecordingNotificationsScreen layout
---
.../molecules/NotificationPresetsRoulette.kt | 72 ++++++++
.../organisms/NotificationEditor.kt | 173 +++++++++++-------
.../CustomRecordingNotificationsScreen.kt | 5 +-
app/src/main/res/values/strings.xml | 1 +
4 files changed, 182 insertions(+), 69 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
new file mode 100644
index 0000000..81bd4bc
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
@@ -0,0 +1,72 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules
+
+import androidx.compose.foundation.ExperimentalFoundationApi
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.Alignment
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
+import androidx.compose.ui.unit.dp
+import app.myzel394.alibi.R
+import app.myzel394.alibi.db.NotificationSettings
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
+
+@OptIn(ExperimentalFoundationApi::class)
+@Composable
+fun NotificationPresetsRoulette(
+ onClick: (String, String, Int, Boolean) -> Unit,
+) {
+ val state = rememberLazyListState()
+
+ LazyRow(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ state = state,
+ flingBehavior = rememberSnapFlingBehavior(lazyListState = state)
+ ) {
+ items(NotificationSettings.PRESETS.size) {
+ val preset = NotificationSettings.PRESETS[it]
+
+ val label = stringResource(
+ R.string.ui_settings_customNotifications_preset_apply_label,
+ stringResource(preset.titleID)
+ )
+ val presetTitle = stringResource(preset.titleID)
+ val presetDescription = stringResource(preset.messageID)
+
+ Box(
+ modifier = Modifier.width(
+ LocalConfiguration.current.screenWidthDp.dp,
+ )
+ ) {
+ NotificationPresetSelect(
+ modifier = Modifier
+ .fillMaxWidth(.95f)
+ .align(Alignment.Center)
+ .semantics {
+ contentDescription = label
+ }
+ .clickable {
+ onClick(
+ presetTitle,
+ presetDescription,
+ preset.iconID,
+ preset.showOngoing,
+ )
+ },
+ preset = preset,
+ )
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index ee1e921..3396077 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -1,28 +1,49 @@
package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms
+import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
+import androidx.compose.foundation.gestures.FlingBehavior
+import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
+import androidx.compose.foundation.layout.width
+import androidx.compose.foundation.lazy.LazyRow
+import androidx.compose.foundation.lazy.rememberLazyListState
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.filled.Check
+import androidx.compose.material.icons.filled.CheckCircle
+import androidx.compose.material.icons.filled.Notifications
+import androidx.compose.material.icons.filled.Save
+import androidx.compose.material3.Button
+import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalConfiguration
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.semantics.contentDescription
@@ -31,10 +52,14 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import app.myzel394.alibi.R
import app.myzel394.alibi.db.NotificationSettings
+import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.NotificationPresetsRoulette
-@OptIn(ExperimentalMaterial3Api::class)
+val HORIZONTAL_PADDING = 16.dp;
+
+@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun NotificationEditor(
modifier: Modifier = Modifier,
@@ -53,93 +78,105 @@ fun NotificationEditor(
mutableStateOf(true)
}
var icon: Int by rememberSaveable {
- mutableStateOf(R.drawable.launcher_monochrome_noopacity)
+ mutableIntStateOf(R.drawable.launcher_monochrome_noopacity)
}
// TODO: Add Preview functionality
Column(
modifier = Modifier
.fillMaxSize()
- .padding(horizontal = 16.dp)
.then(modifier),
verticalArrangement = Arrangement.SpaceBetween,
) {
- EditNotificationInput(
+ Column(
modifier = Modifier
- .fillMaxWidth()
- .padding(horizontal = 16.dp),
- showOngoing = showOngoing,
- title = title,
- description = description,
- icon = painterResource(icon),
- onShowOngoingChange = {
- showOngoing = it
- },
- onTitleChange = {
- title = it
- },
- onDescriptionChange = {
- description = it
- },
- onIconChange = {
- icon = it
- },
- )
-
- Row(
- horizontalArrangement = Arrangement.spacedBy(8.dp),
- verticalAlignment = Alignment.CenterVertically,
- modifier = Modifier
- .fillMaxSize()
- .clip(MaterialTheme.shapes.medium)
- .clickable {
- showOngoing = showOngoing.not()
- }
- .background(MaterialTheme.colorScheme.tertiaryContainer)
- .padding(8.dp),
+ .padding(horizontal = HORIZONTAL_PADDING),
+ verticalArrangement = Arrangement.spacedBy(16.dp),
) {
- Checkbox(
- checked = showOngoing,
- onCheckedChange = {
+ EditNotificationInput(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = 16.dp),
+ showOngoing = showOngoing,
+ title = title,
+ description = description,
+ icon = painterResource(icon),
+ onShowOngoingChange = {
showOngoing = it
},
- colors = CheckboxDefaults.colors(
- checkedColor = MaterialTheme.colorScheme.tertiary,
- checkmarkColor = MaterialTheme.colorScheme.onTertiary,
+ onTitleChange = {
+ title = it
+ },
+ onDescriptionChange = {
+ description = it
+ },
+ onIconChange = {
+ icon = it
+ },
+ )
+
+ Row(
+ horizontalArrangement = Arrangement.spacedBy(8.dp),
+ verticalAlignment = Alignment.CenterVertically,
+ modifier = Modifier
+ .fillMaxWidth()
+ .clip(MaterialTheme.shapes.medium)
+ .clickable {
+ showOngoing = showOngoing.not()
+ }
+ .background(MaterialTheme.colorScheme.tertiaryContainer)
+ .padding(8.dp),
+ ) {
+ Checkbox(
+ checked = showOngoing,
+ onCheckedChange = {
+ showOngoing = it
+ },
+ colors = CheckboxDefaults.colors(
+ checkedColor = MaterialTheme.colorScheme.tertiary,
+ checkmarkColor = MaterialTheme.colorScheme.onTertiary,
+ )
)
- )
- Text(
- text = stringResource(R.string.ui_settings_customNotifications_showOngoing_label),
- style = MaterialTheme.typography.bodyMedium,
- color = MaterialTheme.colorScheme.onTertiaryContainer,
- fontWeight = FontWeight.Bold,
- )
+ Text(
+ text = stringResource(R.string.ui_settings_customNotifications_showOngoing_label),
+ style = MaterialTheme.typography.bodyMedium,
+ color = MaterialTheme.colorScheme.onTertiaryContainer,
+ fontWeight = FontWeight.Bold,
+ )
+ }
}
Column(
- verticalArrangement = Arrangement.spacedBy(4.dp),
+ verticalArrangement = Arrangement.spacedBy(32.dp),
) {
- for (preset in NotificationSettings.PRESETS) {
- val label = stringResource(
- R.string.ui_settings_customNotifications_preset_apply_label,
- stringResource(preset.titleID)
- )
- val presetTitle = stringResource(preset.titleID)
- val presetDescription = stringResource(preset.messageID)
+ NotificationPresetsRoulette(
+ onClick = { presetTitle, presetDescription, presetIcon, presetShowOngoing ->
+ title = presetTitle
+ description = presetDescription
+ icon = presetIcon
+ showOngoing = presetShowOngoing
+ }
+ )
- NotificationPresetSelect(
+ Button(
+ onClick = { /*TODO*/ },
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(horizontal = HORIZONTAL_PADDING)
+ .height(48.dp),
+ ) {
+ Icon(
+ Icons.Default.CheckCircle,
+ contentDescription = null,
modifier = Modifier
- .fillMaxWidth()
- .semantics {
- contentDescription = label
- }
- .clickable {
- title = presetTitle
- description = presetDescription
- icon = preset.iconID
- showOngoing = preset.showOngoing
- },
- preset = preset,
+ .size(ButtonDefaults.IconSize)
+ )
+ Spacer(
+ modifier = Modifier
+ .width(ButtonDefaults.IconSpacing)
+ )
+ Text(
+ stringResource(R.string.ui_settings_customNotifications_save_label)
)
}
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index 7add6e2..a574751 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
+import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
@@ -26,6 +27,8 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.semantics.contentDescription
+import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import androidx.navigation.NavController
import app.myzel394.alibi.R
@@ -77,7 +80,7 @@ fun CustomRecordingNotificationsScreen(
NotificationEditor(
modifier = Modifier
.padding(padding)
- .verticalScroll(scrollState),
+ .padding(vertical = 16.dp),
scrollState = scrollState,
)
}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 3e5c185..40b54d9 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -86,4 +86,5 @@
Connection Secured
Apply Preset \"%s\"
Show Duration
+ Update notification
\ No newline at end of file
From 15bb9b405125242b8dc1a195a1ce8e080ead07a0 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 10:22:05 +0200
Subject: [PATCH 12/24] feat: Improve CustomRecordingNotificationsScreen
behavior; Add save support (wip)
---
.../java/app/myzel394/alibi/db/AppSettings.kt | 11 ++++
.../atoms/LandingElement.kt | 3 +-
.../molecules/NotificationPresetsRoulette.kt | 3 +-
.../organisms/NotificationEditor.kt | 21 +++++--
.../CustomRecordingNotificationsScreen.kt | 58 ++++++++++++++++---
5 files changed, 82 insertions(+), 14 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
index a91b4ab..236d2e6 100644
--- a/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
+++ b/app/src/main/java/app/myzel394/alibi/db/AppSettings.kt
@@ -28,6 +28,10 @@ data class AppSettings(
return copy(audioRecorderSettings = audioRecorderSettings)
}
+ fun setNotificationSettings(notificationSettings: NotificationSettings?): AppSettings {
+ return copy(notificationSettings = notificationSettings)
+ }
+
fun setHasSeenOnboarding(hasSeenOnboarding: Boolean): AppSettings {
return copy(hasSeenOnboarding = hasSeenOnboarding)
}
@@ -418,6 +422,7 @@ data class AudioRecorderSettings(
data class NotificationSettings(
val title: String,
val message: String,
+ val iconID: Int,
val showOngoing: Boolean,
val preset: Preset? = null,
) {
@@ -428,6 +433,7 @@ data class NotificationSettings(
val showOngoing: Boolean,
val iconID: Int,
) {
+ @Serializable
data object Default : Preset(
R.string.ui_audioRecorder_state_recording_title,
R.string.ui_audioRecorder_state_recording_description,
@@ -435,6 +441,7 @@ data class NotificationSettings(
R.drawable.launcher_monochrome_noopacity,
)
+ @Serializable
data object Weather : Preset(
R.string.ui_audioRecorder_state_recording_fake_weather_title,
R.string.ui_audioRecorder_state_recording_fake_weather_description,
@@ -442,6 +449,7 @@ data class NotificationSettings(
R.drawable.ic_cloud
)
+ @Serializable
data object Player : Preset(
R.string.ui_audioRecorder_state_recording_fake_player_title,
R.string.ui_audioRecorder_state_recording_fake_player_description,
@@ -449,6 +457,7 @@ data class NotificationSettings(
R.drawable.ic_note,
)
+ @Serializable
data object Browser : Preset(
R.string.ui_audioRecorder_state_recording_fake_browser_title,
R.string.ui_audioRecorder_state_recording_fake_browser_description,
@@ -456,6 +465,7 @@ data class NotificationSettings(
R.drawable.ic_download,
)
+ @Serializable
data object VPN : Preset(
R.string.ui_audioRecorder_state_recording_fake_vpn_title,
R.string.ui_audioRecorder_state_recording_fake_vpn_description,
@@ -470,6 +480,7 @@ data class NotificationSettings(
title = "",
message = "",
showOngoing = preset.showOngoing,
+ iconID = preset.iconID,
preset = preset,
)
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
index 08cebaa..ae45bf1 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
@@ -36,6 +36,7 @@ import app.myzel394.alibi.ui.utils.openNotificationsSettings
@Composable
fun LandingElement(
modifier: Modifier = Modifier,
+ onOpenEditor: () -> Unit,
) {
val context = LocalContext.current
@@ -82,7 +83,7 @@ fun LandingElement(
style = MaterialTheme.typography.bodySmall,
)
Button(
- onClick = {},
+ onClick = onOpenEditor,
colors = ButtonDefaults.filledTonalButtonColors(),
) {
Icon(
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
index 81bd4bc..9f019fd 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
@@ -24,7 +24,7 @@ import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NotificationPresetsRoulette(
- onClick: (String, String, Int, Boolean) -> Unit,
+ onClick: (String, String, Int, Boolean, NotificationSettings.Preset) -> Unit,
) {
val state = rememberLazyListState()
@@ -62,6 +62,7 @@ fun NotificationPresetsRoulette(
presetDescription,
preset.iconID,
preset.showOngoing,
+ preset,
)
},
preset = preset,
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index 3396077..edf34bc 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -37,6 +37,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
@@ -56,6 +57,7 @@ import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.NotificationPresetsRoulette
+import kotlinx.coroutines.newFixedThreadPoolContext
val HORIZONTAL_PADDING = 16.dp;
@@ -63,7 +65,7 @@ val HORIZONTAL_PADDING = 16.dp;
@Composable
fun NotificationEditor(
modifier: Modifier = Modifier,
- scrollState: ScrollState,
+ onNotificationChange: (String, String, Int, Boolean, NotificationSettings.Preset?) -> Unit,
) {
val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title)
val defaultDescription = stringResource(R.string.ui_audioRecorder_state_recording_description)
@@ -80,8 +82,10 @@ fun NotificationEditor(
var icon: Int by rememberSaveable {
mutableIntStateOf(R.drawable.launcher_monochrome_noopacity)
}
+ var preset: NotificationSettings.Preset? by remember {
+ mutableStateOf(null)
+ }
- // TODO: Add Preview functionality
Column(
modifier = Modifier
.fillMaxSize()
@@ -150,16 +154,25 @@ fun NotificationEditor(
verticalArrangement = Arrangement.spacedBy(32.dp),
) {
NotificationPresetsRoulette(
- onClick = { presetTitle, presetDescription, presetIcon, presetShowOngoing ->
+ onClick = { presetTitle, presetDescription, presetIcon, presetShowOngoing, newPreset ->
title = presetTitle
description = presetDescription
icon = presetIcon
showOngoing = presetShowOngoing
+ preset = newPreset
}
)
Button(
- onClick = { /*TODO*/ },
+ onClick = {
+ onNotificationChange(
+ title,
+ description,
+ icon,
+ showOngoing,
+ preset,
+ )
+ },
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = HORIZONTAL_PADDING)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index a574751..c6c9008 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -22,7 +22,13 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.nestedscroll.nestedScroll
import androidx.compose.ui.platform.LocalContext
@@ -35,9 +41,11 @@ import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.NotificationSettings
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.LandingElement
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms.NotificationEditor
+import kotlinx.coroutines.launch
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@@ -47,7 +55,6 @@ fun CustomRecordingNotificationsScreen(
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
rememberTopAppBarState()
)
- val scrollState = rememberScrollState()
val dataStore = LocalContext.current.dataStore
val settings = dataStore
@@ -55,6 +62,16 @@ fun CustomRecordingNotificationsScreen(
.collectAsState(initial = AppSettings.getDefaultInstance())
.value
+ var showEditor: Boolean by rememberSaveable {
+ mutableStateOf(false)
+ }
+
+ LaunchedEffect(settings.notificationSettings) {
+ if (settings.notificationSettings != null) {
+ showEditor = true
+ }
+ }
+
Scaffold(
topBar = {
TopAppBar(
@@ -75,13 +92,38 @@ fun CustomRecordingNotificationsScreen(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection)
) { padding ->
- if (settings.notificationSettings == null) {
+ if (showEditor) {
+ val scope = rememberCoroutineScope()
+
+ NotificationEditor(
+ modifier = Modifier
+ .padding(padding)
+ .padding(vertical = 16.dp),
+ onNotificationChange = { title, description, icon, showOngoing, preset ->
+ scope.launch {
+ dataStore.updateData { settings ->
+ settings.setNotificationSettings(
+ if (preset == null) {
+ NotificationSettings(
+ title = title,
+ message = description,
+ iconID = icon,
+ showOngoing = showOngoing,
+ )
+ } else {
+ NotificationSettings.fromPreset(preset)
+ }
+ )
+ }
+ }
+ }
+ )
+ } else {
+ LandingElement(
+ onOpenEditor = {
+ showEditor = true
+ }
+ )
}
- NotificationEditor(
- modifier = Modifier
- .padding(padding)
- .padding(vertical = 16.dp),
- scrollState = scrollState,
- )
}
}
\ No newline at end of file
From f1296c32ec3ad411fa44a5e9c4451a24aad5b24b Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 10:48:15 +0200
Subject: [PATCH 13/24] refactor: Migrating to NotificationViewModel
---
.../models/NotificationViewModel.kt | 67 ++++++++++++++
.../molecules/NotificationPresetsRoulette.kt | 4 +-
.../organisms/NotificationEditor.kt | 90 ++++++-------------
3 files changed, 94 insertions(+), 67 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
new file mode 100644
index 0000000..93629f7
--- /dev/null
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
@@ -0,0 +1,67 @@
+package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.models
+
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableIntStateOf
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.setValue
+import androidx.lifecycle.ViewModel
+import app.myzel394.alibi.R
+import app.myzel394.alibi.db.NotificationSettings
+
+class NotificationViewModel : ViewModel() {
+ // We want to show the actual translated strings of the preset
+ // in the preview but don't want to save them to the database
+ // because they should be retrieved in the notification itself.
+ // Thus we save whether the preset has been changed by the user
+ private var _presetChanged = false
+
+ private var _title = mutableStateOf("")
+ val title: String
+ get() = _title.value
+ private var _description = mutableStateOf("")
+ val description: String
+ get() = _description.value
+
+ var showOngoing: Boolean by mutableStateOf(true)
+ var icon: Int by mutableIntStateOf(R.drawable.launcher_monochrome_noopacity)
+
+ // `preset` can't be used as a variable name here because
+ // the compiler throws a strange error then
+ var notificationPreset: NotificationSettings.Preset? by mutableStateOf(null)
+
+ private var _hasBeenInitialized = false;
+
+
+ fun setPreset(title: String, description: String, preset: NotificationSettings.Preset) {
+ _presetChanged = false
+
+ _title.value = title
+ _description.value = description
+ showOngoing = preset.showOngoing
+ icon = preset.iconID
+ this.notificationPreset = preset
+ }
+
+ fun setTitle(title: String) {
+ _presetChanged = true
+ _title.value = title
+ }
+
+ fun setDescription(description: String) {
+ _presetChanged = true
+ _description.value = description
+ }
+
+ fun initialize(
+ title: String,
+ description: String,
+ ) {
+ if (_hasBeenInitialized) {
+ return
+ }
+
+ _title.value = title
+ _description.value = description
+ _hasBeenInitialized = true
+ }
+}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
index 9f019fd..0852cf3 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/molecules/NotificationPresetsRoulette.kt
@@ -24,7 +24,7 @@ import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms
@OptIn(ExperimentalFoundationApi::class)
@Composable
fun NotificationPresetsRoulette(
- onClick: (String, String, Int, Boolean, NotificationSettings.Preset) -> Unit,
+ onClick: (String, String, NotificationSettings.Preset) -> Unit,
) {
val state = rememberLazyListState()
@@ -60,8 +60,6 @@ fun NotificationPresetsRoulette(
onClick(
presetTitle,
presetDescription,
- preset.iconID,
- preset.showOngoing,
preset,
)
},
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index edf34bc..62a04bf 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -1,13 +1,9 @@
package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms
import androidx.compose.foundation.ExperimentalFoundationApi
-import androidx.compose.foundation.ScrollState
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
-import androidx.compose.foundation.gestures.FlingBehavior
-import androidx.compose.foundation.gestures.snapping.rememberSnapFlingBehavior
import androidx.compose.foundation.layout.Arrangement
-import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
@@ -17,47 +13,31 @@ import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
-import androidx.compose.foundation.lazy.LazyRow
-import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.icons.Icons
-import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.filled.CheckCircle
-import androidx.compose.material.icons.filled.Notifications
-import androidx.compose.material.icons.filled.Save
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
-import androidx.compose.material3.CheckboxColors
import androidx.compose.material3.CheckboxDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableIntStateOf
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.remember
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
-import androidx.compose.ui.platform.LocalConfiguration
-import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
-import androidx.compose.ui.semantics.contentDescription
-import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
+import androidx.lifecycle.viewmodel.compose.viewModel
import app.myzel394.alibi.R
import app.myzel394.alibi.db.NotificationSettings
-import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
-import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.atoms.NotificationPresetSelect
+import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.models.NotificationViewModel
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.NotificationPresetsRoulette
-import kotlinx.coroutines.newFixedThreadPoolContext
val HORIZONTAL_PADDING = 16.dp;
@@ -65,25 +45,17 @@ val HORIZONTAL_PADDING = 16.dp;
@Composable
fun NotificationEditor(
modifier: Modifier = Modifier,
+ notificationModel: NotificationViewModel = viewModel(),
onNotificationChange: (String, String, Int, Boolean, NotificationSettings.Preset?) -> Unit,
) {
val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title)
val defaultDescription = stringResource(R.string.ui_audioRecorder_state_recording_description)
- var title: String by rememberSaveable {
- mutableStateOf(defaultTitle)
- }
- var description: String by rememberSaveable {
- mutableStateOf(defaultDescription)
- }
- var showOngoing: Boolean by rememberSaveable {
- mutableStateOf(true)
- }
- var icon: Int by rememberSaveable {
- mutableIntStateOf(R.drawable.launcher_monochrome_noopacity)
- }
- var preset: NotificationSettings.Preset? by remember {
- mutableStateOf(null)
+ LaunchedEffect(defaultTitle, defaultDescription) {
+ notificationModel.initialize(
+ defaultTitle,
+ defaultDescription,
+ )
}
Column(
@@ -101,21 +73,17 @@ fun NotificationEditor(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp),
- showOngoing = showOngoing,
- title = title,
- description = description,
- icon = painterResource(icon),
+ showOngoing = notificationModel.showOngoing,
+ title = notificationModel.title,
+ description = notificationModel.description,
+ icon = painterResource(notificationModel.icon),
onShowOngoingChange = {
- showOngoing = it
- },
- onTitleChange = {
- title = it
- },
- onDescriptionChange = {
- description = it
+ notificationModel.showOngoing = it
},
+ onTitleChange = notificationModel::setTitle,
+ onDescriptionChange = notificationModel::setDescription,
onIconChange = {
- icon = it
+ notificationModel.icon = it
},
)
@@ -126,15 +94,15 @@ fun NotificationEditor(
.fillMaxWidth()
.clip(MaterialTheme.shapes.medium)
.clickable {
- showOngoing = showOngoing.not()
+ notificationModel.showOngoing = notificationModel.showOngoing.not()
}
.background(MaterialTheme.colorScheme.tertiaryContainer)
.padding(8.dp),
) {
Checkbox(
- checked = showOngoing,
+ checked = notificationModel.showOngoing,
onCheckedChange = {
- showOngoing = it
+ notificationModel.showOngoing = it
},
colors = CheckboxDefaults.colors(
checkedColor = MaterialTheme.colorScheme.tertiary,
@@ -154,23 +122,17 @@ fun NotificationEditor(
verticalArrangement = Arrangement.spacedBy(32.dp),
) {
NotificationPresetsRoulette(
- onClick = { presetTitle, presetDescription, presetIcon, presetShowOngoing, newPreset ->
- title = presetTitle
- description = presetDescription
- icon = presetIcon
- showOngoing = presetShowOngoing
- preset = newPreset
- }
+ onClick = notificationModel::setPreset,
)
Button(
onClick = {
onNotificationChange(
- title,
- description,
- icon,
- showOngoing,
- preset,
+ notificationModel.title,
+ notificationModel.description,
+ notificationModel.icon,
+ notificationModel.showOngoing,
+ notificationModel.notificationPreset,
)
},
modifier = Modifier
From 6ad7ff12e6dfa197b95b3c1c7199de85d76a6be2 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 11:31:01 +0200
Subject: [PATCH 14/24] feat: Properly update notification settings
---
.../models/NotificationViewModel.kt | 21 ++++--
.../organisms/NotificationEditor.kt | 66 +++++++++++++++----
.../CustomRecordingNotificationsScreen.kt | 16 +----
3 files changed, 72 insertions(+), 31 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
index 93629f7..2d51797 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/models/NotificationViewModel.kt
@@ -55,13 +55,26 @@ class NotificationViewModel : ViewModel() {
fun initialize(
title: String,
description: String,
+ showOngoing: Boolean = true,
+ icon: Int = R.drawable.launcher_monochrome_noopacity,
) {
- if (_hasBeenInitialized) {
- return
- }
-
_title.value = title
_description.value = description
+ this.showOngoing = showOngoing
+ this.icon = icon
_hasBeenInitialized = true
}
+
+ fun asNotificationSettings(): NotificationSettings {
+ return if (!_presetChanged && notificationPreset != null) {
+ NotificationSettings.fromPreset(notificationPreset!!)
+ } else {
+ NotificationSettings(
+ title = title,
+ message = description,
+ iconID = icon,
+ showOngoing = showOngoing,
+ )
+ }
+ }
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index 62a04bf..43885b2 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -25,15 +25,19 @@ import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.collectAsState
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
+import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.lifecycle.viewmodel.compose.viewModel
import app.myzel394.alibi.R
+import app.myzel394.alibi.dataStore
+import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.NotificationSettings
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.models.NotificationViewModel
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
@@ -41,21 +45,59 @@ import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molec
val HORIZONTAL_PADDING = 16.dp;
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
@Composable
fun NotificationEditor(
modifier: Modifier = Modifier,
notificationModel: NotificationViewModel = viewModel(),
- onNotificationChange: (String, String, Int, Boolean, NotificationSettings.Preset?) -> Unit,
+ onNotificationChange: (NotificationSettings) -> Unit,
) {
- val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title)
- val defaultDescription = stringResource(R.string.ui_audioRecorder_state_recording_description)
+ val dataStore = LocalContext.current.dataStore
+ val settings = dataStore
+ .data
+ .collectAsState(initial = AppSettings.getDefaultInstance())
+ .value
- LaunchedEffect(defaultTitle, defaultDescription) {
- notificationModel.initialize(
- defaultTitle,
- defaultDescription,
- )
+ if (settings.notificationSettings != null) {
+ val title = settings.notificationSettings.let {
+ if (it.preset != null)
+ stringResource(it.preset.titleID)
+ else
+ it.title
+ }
+ val description = settings.notificationSettings.let {
+ if (it.preset != null)
+ stringResource(it.preset.messageID)
+ else
+ it.message
+ }
+
+ LaunchedEffect(Unit) {
+ notificationModel.initialize(
+ title,
+ description,
+ settings.notificationSettings.showOngoing,
+ settings.notificationSettings.iconID,
+ )
+
+ if (settings.notificationSettings.preset != null) {
+ notificationModel.setPreset(
+ title,
+ description,
+ settings.notificationSettings.preset
+ )
+ }
+ }
+ } else {
+ val defaultTitle = stringResource(R.string.ui_audioRecorder_state_recording_title)
+ val defaultDescription =
+ stringResource(R.string.ui_audioRecorder_state_recording_description)
+
+ LaunchedEffect(Unit) {
+ notificationModel.initialize(
+ defaultTitle,
+ defaultDescription,
+ )
+ }
}
Column(
@@ -128,11 +170,7 @@ fun NotificationEditor(
Button(
onClick = {
onNotificationChange(
- notificationModel.title,
- notificationModel.description,
- notificationModel.icon,
- notificationModel.showOngoing,
- notificationModel.notificationPreset,
+ notificationModel.asNotificationSettings()
)
},
modifier = Modifier
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index c6c9008..d854dde 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -99,23 +99,13 @@ fun CustomRecordingNotificationsScreen(
modifier = Modifier
.padding(padding)
.padding(vertical = 16.dp),
- onNotificationChange = { title, description, icon, showOngoing, preset ->
+ onNotificationChange = { notificationSettings ->
scope.launch {
dataStore.updateData { settings ->
- settings.setNotificationSettings(
- if (preset == null) {
- NotificationSettings(
- title = title,
- message = description,
- iconID = icon,
- showOngoing = showOngoing,
- )
- } else {
- NotificationSettings.fromPreset(preset)
- }
- )
+ settings.setNotificationSettings(notificationSettings)
}
}
+ navController.popBackStack()
}
)
} else {
From f68baaf1bf7b334cf006cc0bb34d1cd7cf58527d Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 11:34:47 +0200
Subject: [PATCH 15/24] feat: Set NotificationSettings to `null` if using
default preset
---
.../organisms/NotificationEditor.kt | 2 --
.../alibi/ui/screens/CustomRecordingNotificationsScreen.kt | 7 ++++++-
2 files changed, 6 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index 43885b2..4cd077f 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -1,6 +1,5 @@
package app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.organisms
-import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement
@@ -19,7 +18,6 @@ import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Checkbox
import androidx.compose.material3.CheckboxDefaults
-import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
index d854dde..7956bf3 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/CustomRecordingNotificationsScreen.kt
@@ -102,7 +102,12 @@ fun CustomRecordingNotificationsScreen(
onNotificationChange = { notificationSettings ->
scope.launch {
dataStore.updateData { settings ->
- settings.setNotificationSettings(notificationSettings)
+ settings.setNotificationSettings(notificationSettings.let {
+ if (it.preset == NotificationSettings.Preset.Default)
+ null
+ else
+ it
+ })
}
}
navController.popBackStack()
From 1efb57d54047a3ed414933431e31c6711dd151bf Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 11:40:04 +0200
Subject: [PATCH 16/24] feat: Improve animation
---
app/src/main/java/app/myzel394/alibi/ui/Navigation.kt | 3 ++-
1 file changed, 2 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
index e9c0f99..bd4bca3 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/Navigation.kt
@@ -1,5 +1,6 @@
package app.myzel394.alibi.ui
+import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
@@ -103,7 +104,7 @@ fun Navigation(
exitTransition = {
slideOutHorizontally(
targetOffsetX = { it -> it / 2 }
- ) + fadeOut()
+ ) + fadeOut(tween(150))
}
) {
CustomRecordingNotificationsScreen(
From 81520c48ba4899d21ae3a3f066df062712c132eb Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 12:02:30 +0200
Subject: [PATCH 17/24] refactor: Use RecorderNotificationHelper for
notifications
---
.../services/RecorderNotificationHelper.kt | 121 +++++++++++++++++
.../alibi/services/RecorderService.kt | 127 +++---------------
2 files changed, 139 insertions(+), 109 deletions(-)
create mode 100644 app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
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
From 37a8a9871e2b432db81583b04e8527dd299903ab Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 12:47:46 +0200
Subject: [PATCH 18/24] feat: Add custom notification support
---
.../services/RecorderNotificationHelper.kt | 28 +++++++++-
.../alibi/services/RecorderService.kt | 10 ++++
.../AudioRecorder/molecules/StartRecording.kt | 53 ++++++++++++++++---
.../alibi/ui/models/AudioRecorderModel.kt | 23 ++++++--
.../alibi/ui/screens/AudioRecorder.kt | 6 ++-
5 files changed, 105 insertions(+), 15 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
index 469a8a6..d0835f3 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
@@ -8,7 +8,9 @@ import androidx.core.app.NotificationCompat
import app.myzel394.alibi.MainActivity
import app.myzel394.alibi.NotificationHelper
import app.myzel394.alibi.R
+import app.myzel394.alibi.db.NotificationSettings
import app.myzel394.alibi.enums.RecorderState
+import kotlinx.serialization.Serializable
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.Calendar
@@ -18,12 +20,36 @@ data class RecorderNotificationHelper(
val context: Context,
val details: NotificationDetails? = null,
) {
+ @Serializable
data class NotificationDetails(
val title: String,
val description: String,
val icon: Int,
val isOngoing: Boolean,
- )
+ ) {
+ companion object {
+ fun fromNotificationSettings(
+ context: Context,
+ settings: NotificationSettings,
+ ): NotificationDetails {
+ return if (settings.preset == null) {
+ NotificationDetails(
+ settings.title,
+ settings.message,
+ settings.iconID,
+ settings.showOngoing,
+ )
+ } else {
+ NotificationDetails(
+ context.getString(settings.preset.titleID),
+ context.getString(settings.preset.messageID),
+ settings.preset.iconID,
+ settings.preset.showOngoing,
+ )
+ }
+ }
+ }
+ }
private fun getNotificationChangeStateIntent(
newState: RecorderState,
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 6adfa17..750d3ea 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
@@ -14,6 +14,7 @@ import app.myzel394.alibi.NotificationHelper
import app.myzel394.alibi.R
import app.myzel394.alibi.enums.RecorderState
import app.myzel394.alibi.ui.utils.PermissionHelper
+import kotlinx.serialization.json.Json
import java.time.LocalDateTime
import java.time.ZoneId
import java.util.Calendar
@@ -51,6 +52,15 @@ abstract class RecorderService : Service() {
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
when (intent?.action) {
+ "init" -> {
+ notificationDetails = intent.getStringExtra("notificationDetails")?.let {
+ Json.decodeFromString(
+ RecorderNotificationHelper.NotificationDetails.serializer(),
+ it
+ )
+ }
+ }
+
"changeState" -> {
val newState = intent.getStringExtra("newState")?.let {
RecorderState.valueOf(it)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt
index a040e48..0c3c046 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/AudioRecorder/molecules/StartRecording.kt
@@ -20,8 +20,15 @@ import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
+import androidx.compose.material3.rememberStandardBottomSheetState
import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.rememberCoroutineScope
+import androidx.compose.runtime.saveable.rememberSaveable
+import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
@@ -31,22 +38,51 @@ import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
+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.services.RecorderNotificationHelper
import app.myzel394.alibi.ui.BIG_PRIMARY_BUTTON_SIZE
import app.myzel394.alibi.ui.components.atoms.PermissionRequester
import app.myzel394.alibi.ui.models.AudioRecorderModel
import app.myzel394.alibi.ui.utils.rememberFileSaverDialog
+import kotlinx.coroutines.flow.last
+import kotlinx.coroutines.flow.lastOrNull
+import kotlinx.coroutines.launch
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle
@Composable
fun StartRecording(
audioRecorder: AudioRecorderModel,
+ // Loading this from parent, because if we load it ourselves
+ // and permissions have already been granted, initial
+ // settings will be used, instead of the actual settings.
+ appSettings: AppSettings
) {
val context = LocalContext.current
+ // We can't get the current `notificationDetails` inside the
+ // `onPermissionAvailable` function. We'll instead use this hack
+ // with `LaunchedEffect` to get the current value.
+ var startRecording by rememberSaveable { mutableStateOf(false) }
+ LaunchedEffect(startRecording) {
+ if (startRecording) {
+ startRecording = false
+ audioRecorder.notificationDetails = appSettings.notificationSettings.let {
+ if (it == null)
+ null
+ else
+ RecorderNotificationHelper.NotificationDetails.fromNotificationSettings(
+ context,
+ it
+ )
+ }
+ audioRecorder.startRecording(context)
+ }
+ }
+
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.SpaceBetween,
@@ -57,14 +93,12 @@ fun StartRecording(
permission = Manifest.permission.RECORD_AUDIO,
icon = Icons.Default.Mic,
onPermissionAvailable = {
- audioRecorder.startRecording(context)
+ startRecording = true
},
) { trigger ->
val label = stringResource(R.string.ui_audioRecorder_action_start_label)
Button(
- onClick = {
- trigger()
- },
+ onClick = trigger,
modifier = Modifier
.semantics {
contentDescription = label
@@ -98,7 +132,10 @@ fun StartRecording(
.value
Text(
- stringResource(R.string.ui_audioRecorder_action_start_description, settings.audioRecorderSettings.maxDuration / 1000 / 60),
+ stringResource(
+ R.string.ui_audioRecorder_action_start_description,
+ settings.audioRecorderSettings.maxDuration / 1000 / 60
+ ),
style = MaterialTheme.typography.bodySmall.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
),
@@ -115,7 +152,8 @@ fun StartRecording(
) {
val label = stringResource(
R.string.ui_audioRecorder_action_saveOldRecording_label,
- DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL).format(audioRecorder.lastRecording!!.recordingStart),
+ DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL)
+ .format(audioRecorder.lastRecording!!.recordingStart),
)
Button(
modifier = Modifier
@@ -140,8 +178,7 @@ fun StartRecording(
Text(label)
}
}
- }
- else
+ } else
Spacer(modifier = Modifier.weight(1f))
}
}
\ No newline at end of file
diff --git a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
index 3b666cb..7e278ee 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/models/AudioRecorderModel.kt
@@ -13,10 +13,14 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModel
+import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.LastRecording
import app.myzel394.alibi.enums.RecorderState
import app.myzel394.alibi.services.AudioRecorderService
+import app.myzel394.alibi.services.RecorderNotificationHelper
import app.myzel394.alibi.services.RecorderService
+import kotlinx.coroutines.flow.last
+import kotlinx.serialization.json.Json
class AudioRecorderModel : ViewModel() {
var recorderState by mutableStateOf(RecorderState.IDLE)
@@ -45,6 +49,7 @@ class AudioRecorderModel : ViewModel() {
var onRecordingSave: () -> Unit = {}
var onError: () -> Unit = {}
+ var notificationDetails: RecorderNotificationHelper.NotificationDetails? = null
private val connection = object : ServiceConnection {
override fun onServiceConnected(className: ComponentName, service: IBinder) {
@@ -90,7 +95,19 @@ class AudioRecorderModel : ViewModel() {
context.unbindService(connection)
}
- val intent = Intent(context, AudioRecorderService::class.java)
+ val intent = Intent(context, AudioRecorderService::class.java).apply {
+ action = "init"
+
+ if (notificationDetails != null) {
+ putExtra(
+ "notificationDetails",
+ Json.encodeToString(
+ RecorderNotificationHelper.NotificationDetails.serializer(),
+ notificationDetails!!,
+ ),
+ )
+ }
+ }
ContextCompat.startForegroundService(context, intent)
context.bindService(intent, connection, Context.BIND_AUTO_CREATE)
}
@@ -118,10 +135,6 @@ class AudioRecorderModel : ViewModel() {
recorderService!!.changeState(RecorderState.RECORDING)
}
- fun setNotificationDetails(details: RecorderService.NotificationDetails) {
- recorderService?.notificationDetails = details
- }
-
fun setMaxAmplitudesAmount(amount: Int) {
recorderService?.amplitudesAmount = amount
}
diff --git a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt
index 1e121bf..99845dc 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/screens/AudioRecorder.kt
@@ -38,6 +38,7 @@ import app.myzel394.alibi.R
import app.myzel394.alibi.dataStore
import app.myzel394.alibi.db.AppSettings
import app.myzel394.alibi.db.LastRecording
+import app.myzel394.alibi.services.RecorderNotificationHelper
import app.myzel394.alibi.ui.effects.rememberSettings
import app.myzel394.alibi.ui.models.AudioRecorderModel
import kotlinx.coroutines.launch
@@ -171,10 +172,13 @@ fun AudioRecorder(
.fillMaxSize()
.padding(padding),
) {
+ val appSettings =
+ context.dataStore.data.collectAsState(AppSettings.getDefaultInstance()).value
+
if (audioRecorder.isInRecording)
RecordingStatus(audioRecorder = audioRecorder)
else
- StartRecording(audioRecorder = audioRecorder)
+ StartRecording(audioRecorder = audioRecorder, appSettings = appSettings)
}
}
}
\ No newline at end of file
From 26f17869d67cec030e78df1b45087b862b95a217 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 12:50:58 +0200
Subject: [PATCH 19/24] feat: Add message info to NotificationEditor
---
.../organisms/NotificationEditor.kt | 7 +++++++
.../myzel394/alibi/ui/components/atoms/MessageBox.kt | 10 +++++++---
app/src/main/res/values/strings.xml | 1 +
3 files changed, 15 insertions(+), 3 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
index 4cd077f..7aacc97 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/organisms/NotificationEditor.kt
@@ -40,6 +40,8 @@ import app.myzel394.alibi.db.NotificationSettings
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.models.NotificationViewModel
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.EditNotificationInput
import app.myzel394.alibi.ui.components.CustomRecordingNotificationsScreen.molecules.NotificationPresetsRoulette
+import app.myzel394.alibi.ui.components.atoms.MessageBox
+import app.myzel394.alibi.ui.components.atoms.MessageType
val HORIZONTAL_PADDING = 16.dp;
@@ -109,6 +111,11 @@ fun NotificationEditor(
.padding(horizontal = HORIZONTAL_PADDING),
verticalArrangement = Arrangement.spacedBy(16.dp),
) {
+ MessageBox(
+ type = MessageType.SURFACE,
+ message = stringResource(R.string.ui_settings_customNotifications_edit_help)
+ )
+
EditNotificationInput(
modifier = Modifier
.fillMaxWidth()
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt
index cf0f0c9..1c594ff 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/atoms/MessageBox.kt
@@ -29,17 +29,19 @@ fun MessageBox(
title: String? = null,
) {
val isDark = rememberIsInDarkMode()
- val containerColor = when(type) {
+ val containerColor = when (type) {
MessageType.ERROR -> MaterialTheme.colorScheme.errorContainer
MessageType.INFO -> MaterialTheme.colorScheme.tertiaryContainer
MessageType.SUCCESS -> Color.Green.copy(alpha = 0.3f)
MessageType.WARNING -> Color.Yellow.copy(alpha = 0.3f)
+ MessageType.SURFACE -> MaterialTheme.colorScheme.surfaceVariant
}
- val onContainerColor = when(type) {
+ val onContainerColor = when (type) {
MessageType.ERROR -> MaterialTheme.colorScheme.onError
MessageType.INFO -> MaterialTheme.colorScheme.onTertiaryContainer
MessageType.SUCCESS -> Color.Green
MessageType.WARNING -> Color.Yellow
+ MessageType.SURFACE -> MaterialTheme.colorScheme.onSurfaceVariant
}
val textColor = if (isDark) onContainerColor else MaterialTheme.colorScheme.onSurface
val backgroundColor = if (isDark) containerColor else onContainerColor
@@ -53,9 +55,10 @@ fun MessageBox(
.then(modifier)
) {
Icon(
- imageVector = when(type) {
+ imageVector = when (type) {
MessageType.ERROR -> Icons.Default.Error
MessageType.INFO -> Icons.Default.Info
+ MessageType.SURFACE -> Icons.Default.Info
MessageType.SUCCESS -> Icons.Default.Check
MessageType.WARNING -> Icons.Default.Warning
},
@@ -84,6 +87,7 @@ fun MessageBox(
enum class MessageType {
ERROR,
INFO,
+ SURFACE,
SUCCESS,
WARNING,
}
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 40b54d9..33e9c64 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -87,4 +87,5 @@
Apply Preset \"%s\"
Show Duration
Update notification
+ This is a preview for your notification. You can edit the title and the message. At the bottom you can find some presets.
\ No newline at end of file
From 573a70cca841e20e9f58b11dd3924e01f09e55f1 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 14:02:37 +0200
Subject: [PATCH 20/24] fix: Fix padding
---
.../CustomRecordingNotificationsScreen/atoms/LandingElement.kt | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
index ae45bf1..01c5f1e 100644
--- a/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
+++ b/app/src/main/java/app/myzel394/alibi/ui/components/CustomRecordingNotificationsScreen/atoms/LandingElement.kt
@@ -43,7 +43,7 @@ fun LandingElement(
Column(
modifier = Modifier
.fillMaxSize()
- .padding(horizontal = 32.dp)
+ .padding(horizontal = 32.dp, vertical = 64.dp)
.then(modifier),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.SpaceBetween,
From c6d20c74f27393a57aa5b79266965cfcda632a57 Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 14:11:56 +0200
Subject: [PATCH 21/24] fix: Properly set on going for notifications
---
.../app/myzel394/alibi/services/RecorderNotificationHelper.kt | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
index d0835f3..86ad9bb 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderNotificationHelper.kt
@@ -98,9 +98,9 @@ data class RecorderNotificationHelper(
fun buildRecordingNotification(recordingTime: Long): Notification {
return createBaseNotification()
- .setUsesChronometer(true)
+ .setUsesChronometer(details?.isOngoing ?: true)
.setOngoing(details?.isOngoing ?: true)
- .setShowWhen(true)
+ .setShowWhen(details?.isOngoing ?: true)
.setWhen(
Date.from(
Calendar
From f80d3cbe16ec10f19a5b0ed3c8527c85533a2e7b Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Sat, 21 Oct 2023 19:17:51 +0200
Subject: [PATCH 22/24] fix(ci-cd): Upload all APKs
---
.github/workflows/build-testing.yaml | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/.github/workflows/build-testing.yaml b/.github/workflows/build-testing.yaml
index c2793e9..680ea90 100644
--- a/.github/workflows/build-testing.yaml
+++ b/.github/workflows/build-testing.yaml
@@ -25,4 +25,4 @@ jobs:
- name: Upload APK
uses: actions/upload-artifact@v3
with:
- path: app/build/outputs/apk/debug/app-debug.apk
+ path: app/build/outputs/apk/debug/app-*-debug.apk
From cb43a1b37199c08aa33231ed5f6173a1ff4e62ad Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 15:43:51 +0200
Subject: [PATCH 23/24] chore: Adapt to new foreground service
---
.../app/myzel394/alibi/services/RecorderService.kt | 14 +++++++++++++-
1 file changed, 13 insertions(+), 1 deletion(-)
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 750d3ea..1024f87 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
@@ -5,10 +5,13 @@ import android.app.Notification
import android.app.PendingIntent
import android.app.Service
import android.content.Intent
+import android.content.pm.ServiceInfo
import android.os.Binder
+import android.os.Build
import android.os.IBinder
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
+import androidx.core.app.ServiceCompat
import app.myzel394.alibi.MainActivity
import app.myzel394.alibi.NotificationHelper
import app.myzel394.alibi.R
@@ -150,7 +153,16 @@ abstract class RecorderService : Service() {
recordingStart = LocalDateTime.now()
val notification = getNotificationHelper().buildStartingNotification()
- startForeground(NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID, notification)
+ ServiceCompat.startForeground(
+ this,
+ NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID,
+ notification,
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
+ } else {
+ 0
+ },
+ )
// Start
changeState(RecorderState.RECORDING)
From 16e6fd8fc208e238a7200e096ea279691505bc7a Mon Sep 17 00:00:00 2001
From: Myzel394 <50424412+Myzel394@users.noreply.github.com>
Date: Tue, 24 Oct 2023 15:49:15 +0200
Subject: [PATCH 24/24] fix: Improve code
---
.../main/java/app/myzel394/alibi/services/RecorderService.kt | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
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 1024f87..f3a24ce 100644
--- a/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
+++ b/app/src/main/java/app/myzel394/alibi/services/RecorderService.kt
@@ -152,11 +152,10 @@ abstract class RecorderService : Service() {
fun startRecording() {
recordingStart = LocalDateTime.now()
- val notification = getNotificationHelper().buildStartingNotification()
ServiceCompat.startForeground(
this,
NotificationHelper.RECORDER_CHANNEL_NOTIFICATION_ID,
- notification,
+ getNotificationHelper().buildStartingNotification(),
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
} else {