diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/ServiceIconPreviewNotifications.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/ServiceIconPreviewNotifications.kt new file mode 100644 index 0000000000..a1398ce5a0 --- /dev/null +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/service/notifications/ServiceIconPreviewNotifications.kt @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2025 Vitor Pamplona + * + * Permission is hereby granted, free of charge, to any person obtaining a copy of + * this software and associated documentation files (the "Software"), to deal in + * the Software without restriction, including without limitation the rights to use, + * copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the + * Software, and to permit persons to whom the Software is furnished to do so, + * subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS + * FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR + * COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN + * AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + */ +package com.vitorpamplona.amethyst.service.notifications + +// ===================================================================================== +// TEMPORARY / DEBUG-ONLY — REMOVE BEFORE MERGE. +// +// Posts one always-on-style notification per candidate small-icon so the different +// "service icon" designs can be compared side by side in the status bar / shade on a +// real device. These mimic the real NotificationRelayService notification (same +// channel, ongoing, silent, low priority) but each uses a different drawable and a +// label naming the design. +// +// To remove the whole experiment later: delete this file, the four extra drawables +// (amethyst_service2..6), and the single ServiceIconPreviewNotifications.postAll(...) +// call in MainActivity.onCreate. +// ===================================================================================== + +import android.app.NotificationChannel +import android.app.NotificationManager +import android.content.Context +import android.os.Build +import androidx.annotation.DrawableRes +import androidx.core.app.NotificationCompat +import androidx.core.app.NotificationManagerCompat +import com.vitorpamplona.amethyst.R + +object ServiceIconPreviewNotifications { + private const val CHANNEL_ID = "service_icon_preview" + private const val BASE_ID = 920_000 + + private data class Candidate( + @DrawableRes val icon: Int, + val label: String, + ) + + private val candidates = + listOf( + Candidate(R.drawable.amethyst_service, "1 · Hollow gem outline (current)"), + Candidate(R.drawable.amethyst_service2, "2 · Solid circle, gem cut-out"), + Candidate(R.drawable.amethyst_service3, "3 · Orbit + server nodes"), + Candidate(R.drawable.amethyst_service4, "4 · Sync arrows (running)"), + Candidate(R.drawable.amethyst_service5, "5 · Hub & spoke (connected)"), + Candidate(R.drawable.amethyst_service6, "6 · Broadcast waves"), + ) + + fun postAll(context: Context) { + val manager = NotificationManagerCompat.from(context) + if (!manager.areNotificationsEnabled()) return + + ensureChannel(context) + + candidates.forEachIndexed { index, candidate -> + val notification = + NotificationCompat + .Builder(context, CHANNEL_ID) + .setContentTitle("Service icon ${index + 1}") + .setContentText(candidate.label) + .setSmallIcon(candidate.icon) + .setOngoing(true) + .setSilent(true) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setCategory(NotificationCompat.CATEGORY_SERVICE) + .build() + + manager.notify(BASE_ID + index, notification) + } + } + + fun clearAll(context: Context) { + val manager = NotificationManagerCompat.from(context) + candidates.indices.forEach { manager.cancel(BASE_ID + it) } + } + + private fun ensureChannel(context: Context) { + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) return + val channel = + NotificationChannel( + CHANNEL_ID, + "Service icon preview (debug)", + NotificationManager.IMPORTANCE_LOW, + ).apply { + description = "Temporary side-by-side preview of always-on service icons" + setShowBadge(false) + } + val notificationManager = + context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager + notificationManager.createNotificationChannel(channel) + } +} diff --git a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt index edf9d17fbf..9a594cfa42 100644 --- a/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt +++ b/amethyst/src/main/java/com/vitorpamplona/amethyst/ui/MainActivity.kt @@ -27,11 +27,13 @@ import androidx.activity.enableEdgeToEdge import androidx.annotation.RequiresApi import androidx.appcompat.app.AppCompatActivity import com.vitorpamplona.amethyst.Amethyst +import com.vitorpamplona.amethyst.BuildConfig import com.vitorpamplona.amethyst.debugState import com.vitorpamplona.amethyst.model.Account import com.vitorpamplona.amethyst.model.LocalCache import com.vitorpamplona.amethyst.service.lang.LanguageTranslatorService import com.vitorpamplona.amethyst.service.notifications.NotificationRelayService +import com.vitorpamplona.amethyst.service.notifications.ServiceIconPreviewNotifications import com.vitorpamplona.amethyst.service.playback.composable.DEFAULT_MUTED_SETTING import com.vitorpamplona.amethyst.service.playback.pip.BackgroundMedia import com.vitorpamplona.amethyst.ui.navigation.findParameterValue @@ -78,6 +80,13 @@ class MainActivity : AppCompatActivity() { Log.d("ActivityLifecycle") { "MainActivity.onCreate $this" } + // TEMPORARY / DEBUG-ONLY — REMOVE BEFORE MERGE. + // Posts side-by-side preview notifications for the candidate always-on + // service icons so they can be compared on a real device. + if (BuildConfig.DEBUG) { + ServiceIconPreviewNotifications.postAll(this) + } + setContent { StringResSetup() AmethystTheme { diff --git a/amethyst/src/main/res/drawable/amethyst_service3.xml b/amethyst/src/main/res/drawable/amethyst_service3.xml new file mode 100644 index 0000000000..4eeb6cf4fa --- /dev/null +++ b/amethyst/src/main/res/drawable/amethyst_service3.xml @@ -0,0 +1,30 @@ + + + + + + + + diff --git a/amethyst/src/main/res/drawable/amethyst_service4.xml b/amethyst/src/main/res/drawable/amethyst_service4.xml new file mode 100644 index 0000000000..6ebb841d09 --- /dev/null +++ b/amethyst/src/main/res/drawable/amethyst_service4.xml @@ -0,0 +1,35 @@ + + + + + + + + diff --git a/amethyst/src/main/res/drawable/amethyst_service5.xml b/amethyst/src/main/res/drawable/amethyst_service5.xml new file mode 100644 index 0000000000..54bde2c604 --- /dev/null +++ b/amethyst/src/main/res/drawable/amethyst_service5.xml @@ -0,0 +1,65 @@ + + + + + + + + + + + + + + diff --git a/amethyst/src/main/res/drawable/amethyst_service6.xml b/amethyst/src/main/res/drawable/amethyst_service6.xml new file mode 100644 index 0000000000..be905d5e97 --- /dev/null +++ b/amethyst/src/main/res/drawable/amethyst_service6.xml @@ -0,0 +1,57 @@ + + + + + + + + + +