feat(android): add foreground service, Quick Settings tile, proguard rules

This commit is contained in:
saravanakumardb1 2026-02-27 23:12:18 -08:00
parent 449b2dc514
commit 4570c076ec
4 changed files with 162 additions and 0 deletions

21
android/app/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,21 @@
# ChronoMind ProGuard Rules
# Keep Hilt generated classes
-keep class dagger.hilt.** { *; }
-keep class javax.inject.** { *; }
-keep class * extends dagger.hilt.android.internal.managers.ViewComponentManager$FragmentContextWrapper { *; }
# Keep Kotlin serialization
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.AnnotationsKt
-keepclassmembers class kotlinx.serialization.json.** { *** Companion; }
-keepclasseswithmembers class kotlinx.serialization.json.** { kotlinx.serialization.KSerializer serializer(...); }
-keep,includedescriptorclasses class com.chronomind.app.**$$serializer { *; }
-keepclassmembers class com.chronomind.app.** { *** Companion; }
-keepclasseswithmembers class com.chronomind.app.** { kotlinx.serialization.KSerializer serializer(...); }
# Keep Glance widget receivers
-keep class com.chronomind.app.widget.** { *; }
# Keep notification receivers
-keep class com.chronomind.app.notifications.** { *; }

View File

@ -69,6 +69,24 @@
</intent-filter>
</receiver>
<!-- Foreground Service for active timer display -->
<service
android:name=".service.TimerForegroundService"
android:exported="false"
android:foregroundServiceType="specialUse" />
<!-- Quick Settings Tile -->
<service
android:name=".service.QuickTimerTileService"
android:exported="true"
android:icon="@android:drawable/ic_lock_idle_alarm"
android:label="Quick Timer"
android:permission="android.permission.BIND_QUICK_SETTINGS_TILE">
<intent-filter>
<action android:name="android.service.quicksettings.action.QS_TILE" />
</intent-filter>
</service>
<!-- Widgets -->
<receiver
android:name=".widget.TimerWidgetSmallReceiver"

View File

@ -0,0 +1,27 @@
package com.chronomind.app.service
import android.content.Intent
import android.service.quicksettings.TileService
import com.chronomind.app.MainActivity
class QuickTimerTileService : TileService() {
override fun onClick() {
super.onClick()
// Open app to create timer screen
val intent = Intent(this, MainActivity::class.java).apply {
flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP
putExtra("action", "quick_timer")
}
startActivityAndCollapse(intent)
}
override fun onStartListening() {
super.onStartListening()
qsTile?.let { tile ->
tile.label = "Quick Timer"
tile.subtitle = "Create timer"
tile.updateTile()
}
}
}

View File

@ -0,0 +1,96 @@
package com.chronomind.app.service
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.Context
import android.content.Intent
import android.os.IBinder
import androidx.core.app.NotificationCompat
import com.chronomind.app.MainActivity
class TimerForegroundService : Service() {
companion object {
const val CHANNEL_ID = "chronomind_foreground"
const val NOTIFICATION_ID = 9999
const val EXTRA_TIMER_LABEL = "timer_label"
const val EXTRA_TIMER_REMAINING = "timer_remaining"
const val ACTION_STOP = "com.chronomind.STOP_FOREGROUND"
fun start(context: Context, label: String, remainingFormatted: String) {
val intent = Intent(context, TimerForegroundService::class.java).apply {
putExtra(EXTRA_TIMER_LABEL, label)
putExtra(EXTRA_TIMER_REMAINING, remainingFormatted)
}
context.startForegroundService(intent)
}
fun stop(context: Context) {
context.stopService(Intent(context, TimerForegroundService::class.java))
}
}
override fun onCreate() {
super.onCreate()
createChannel()
}
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
if (intent?.action == ACTION_STOP) {
stopForeground(STOP_FOREGROUND_REMOVE)
stopSelf()
return START_NOT_STICKY
}
val label = intent?.getStringExtra(EXTRA_TIMER_LABEL) ?: "Timer"
val remaining = intent?.getStringExtra(EXTRA_TIMER_REMAINING) ?: "--:--"
val notification = buildNotification(label, remaining)
startForeground(NOTIFICATION_ID, notification)
return START_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
private fun createChannel() {
val channel = NotificationChannel(
CHANNEL_ID,
"Active Timer",
NotificationManager.IMPORTANCE_LOW
).apply {
description = "Shows countdown for the active timer"
setShowBadge(false)
}
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
manager.createNotificationChannel(channel)
}
private fun buildNotification(label: String, remaining: String): Notification {
val openIntent = PendingIntent.getActivity(
this, 0,
Intent(this, MainActivity::class.java),
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
val stopIntent = PendingIntent.getService(
this, 1,
Intent(this, TimerForegroundService::class.java).apply { action = ACTION_STOP },
PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
)
return NotificationCompat.Builder(this, CHANNEL_ID)
.setSmallIcon(android.R.drawable.ic_lock_idle_alarm)
.setContentTitle(label)
.setContentText(remaining)
.setOngoing(true)
.setSilent(true)
.setContentIntent(openIntent)
.addAction(android.R.drawable.ic_menu_close_clear_cancel, "Stop", stopIntent)
.setCategory(NotificationCompat.CATEGORY_SERVICE)
.build()
}
}