Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 17 additions & 17 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ android {
minSdk 31
targetSdk 36
versionCode 5
versionName "2025.09.07"
versionName "2025.09.21"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}

Expand Down Expand Up @@ -140,42 +140,42 @@ dependencies {
implementation 'com.google.android.material:material:1.13.0'
implementation 'androidx.constraintlayout:constraintlayout:2.2.1'

implementation 'androidx.activity:activity-ktx:1.10.1'
implementation 'androidx.activity:activity-ktx:1.11.0'
implementation 'androidx.databinding:databinding-runtime:8.13.0'
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.3'
implementation 'androidx.activity:activity-compose:1.10.1'
implementation platform('androidx.compose:compose-bom:2025.08.01')
implementation 'androidx.lifecycle:lifecycle-runtime-ktx:2.9.4'
implementation 'androidx.activity:activity-compose:1.11.0'
implementation platform('androidx.compose:compose-bom:2025.09.00')
implementation 'androidx.compose.ui:ui'
implementation 'androidx.compose.ui:ui-graphics'
implementation 'androidx.compose.ui:ui-tooling-preview'
implementation 'androidx.compose.material3:material3:1.4.0-beta03'
androidTestImplementation platform('androidx.compose:compose-bom:2025.08.01')
implementation 'androidx.compose.material3:material3:1.4.0-rc01'
androidTestImplementation platform('androidx.compose:compose-bom:2025.09.00')
androidTestImplementation 'androidx.compose.ui:ui-test-junit4'
debugImplementation 'androidx.compose.ui:ui-tooling'
debugImplementation 'androidx.compose.ui:ui-test-manifest'

// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.3"
implementation "androidx.lifecycle:lifecycle-common-java8:2.9.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.3"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.3"
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.9.3'
implementation "androidx.lifecycle:lifecycle-livedata-ktx:2.9.4"
implementation "androidx.lifecycle:lifecycle-common-java8:2.9.4"
implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:2.9.4"
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:2.9.4"
implementation 'androidx.lifecycle:lifecycle-viewmodel-compose:2.9.4'

implementation "com.google.dagger:hilt-android:2.57.1"
ksp "com.google.dagger:hilt-compiler:2.57.1"


implementation "androidx.room:room-runtime:2.7.2"
annotationProcessor "androidx.room:room-compiler:2.7.2"
implementation 'androidx.room:room-ktx:2.7.2'
ksp "androidx.room:room-compiler:2.7.2"
implementation "androidx.room:room-runtime:2.8.0"
annotationProcessor "androidx.room:room-compiler:2.8.0"
implementation 'androidx.room:room-ktx:2.8.0'
ksp "androidx.room:room-compiler:2.8.0"

implementation "androidx.datastore:datastore-preferences:1.2.0-alpha02"

implementation("com.google.accompanist:accompanist-permissions:0.37.3")
implementation "androidx.compose.material:material-icons-extended:1.7.8"

implementation "androidx.work:work-runtime-ktx:2.10.3"
implementation "androidx.work:work-runtime-ktx:2.10.4"

testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.3.0'
Expand Down
2 changes: 1 addition & 1 deletion app/src/main/java/com/maary/liveinpeace/Constants.kt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ class Constants {
// 提醒时间
const val ALERT_TIME: Long = 2*60*60*1000
// 延后时间
const val DEBOUNCE_TIME_MS = 500
const val THROTTLE_TIME_MS = 100L
// 不同通知的 GROUP ID
const val ID_NOTIFICATION_GROUP_FORE = "LIP_notification_group_foreground"
const val ID_NOTIFICATION_GROUP_ALERTS = "LIP_notification_group_alerts"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,15 +29,20 @@ class MuteMediaReceiver: BroadcastReceiver() {
if (p1?.action == BROADCAST_ACTION_SLEEPTIMER_CANCEL ||
p1?.action == BROADCAST_ACTION_SLEEPTIMER_INCREMENT ||
p1?.action == BROADCAST_ACTION_SLEEPTIMER_DECREMENT) {

p0?.handle(p1)
val intent = Intent(BROADCAST_ACTION_SLEEPTIMER_UPDATE)
p0?.sendBroadcast(intent)

val intent = Intent(p0, ForegroundService::class.java)
intent.action = BROADCAST_ACTION_SLEEPTIMER_UPDATE
p0?.startService(intent)
}

if (p1?.action == BROADCAST_ACTION_SLEEPTIMER_TOGGLE) {
p0?.toggle()
val intent = Intent(BROADCAST_ACTION_SLEEPTIMER_UPDATE)
p0?.sendBroadcast(intent)

val intent = Intent(p0, ForegroundService::class.java)
intent.action = BROADCAST_ACTION_SLEEPTIMER_UPDATE
p0?.startService(intent)
}
Comment on lines 29 to 46

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The logic for handling sleep timer actions involves creating an Intent and calling startService in two separate if blocks, which leads to code duplication. You can refactor this using a when statement to handle the different actions and then call startService once. This will make the code more concise and easier to maintain.

        val action = p1?.action
        val sleepTimerActionHandled = when (action) {
            BROADCAST_ACTION_SLEEPTIMER_CANCEL,
            BROADCAST_ACTION_SLEEPTIMER_INCREMENT,
            BROADCAST_ACTION_SLEEPTIMER_DECREMENT -> {
                p0?.handle(p1)
                true
            }
            BROADCAST_ACTION_SLEEPTIMER_TOGGLE -> {
                p0?.toggle()
                true
            }
            else -> false
        }

        if (sleepTimerActionHandled) {
            val intent = Intent(p0, ForegroundService::class.java).apply {
                this.action = BROADCAST_ACTION_SLEEPTIMER_UPDATE
            }
            p0?.startService(intent)
        }

}
}
18 changes: 0 additions & 18 deletions app/src/main/java/com/maary/liveinpeace/receiver/SleepReceiver.kt

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -3,23 +3,17 @@ package com.maary.liveinpeace.receiver
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.os.Handler
import android.os.Looper
import com.maary.liveinpeace.Constants.Companion.DEBOUNCE_TIME_MS
import com.maary.liveinpeace.Constants.Companion.THROTTLE_TIME_MS

abstract class VolumeReceiver : BroadcastReceiver() {
private var lastUpdateTime: Long = 0
private val handler = Handler(Looper.getMainLooper())

override fun onReceive(context: Context, intent: Intent) {
if (intent.action == "android.media.VOLUME_CHANGED_ACTION"
) {
if (intent.action == "android.media.VOLUME_CHANGED_ACTION") {
val now = System.currentTimeMillis()
if (now - lastUpdateTime >= DEBOUNCE_TIME_MS) {
if (now - lastUpdateTime >= THROTTLE_TIME_MS) {
lastUpdateTime = now
updateNotification(context)
} else {
handler.postDelayed({ onReceive(context, intent) }, DEBOUNCE_TIME_MS.toLong())
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@ import android.media.AudioManager
import android.os.Build
import android.os.IBinder
import android.util.Log
import android.util.SparseIntArray
import androidx.core.app.ActivityCompat
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.graphics.drawable.IconCompat
import com.maary.liveinpeace.Constants
import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_UPDATE
import com.maary.liveinpeace.DeviceTimer
import com.maary.liveinpeace.R
import com.maary.liveinpeace.SleepNotification.find
Expand All @@ -32,7 +34,6 @@ import com.maary.liveinpeace.database.ConnectionDao
import com.maary.liveinpeace.database.ConnectionRoomDatabase
import com.maary.liveinpeace.database.PreferenceRepository
import com.maary.liveinpeace.receiver.MuteMediaReceiver
import com.maary.liveinpeace.receiver.SleepReceiver
import com.maary.liveinpeace.receiver.VolumeReceiver
import dagger.hilt.android.AndroidEntryPoint
import jakarta.inject.Inject
Expand Down Expand Up @@ -111,11 +112,14 @@ class ForegroundService : Service() {
private val protectionJobs = ConcurrentHashMap<String, Job>()
private val deviceMapMutex = Mutex()

private val volumeIconMap = SparseIntArray()

override fun onCreate() {
super.onCreate()
Log.d(TAG, "Service creating...")

initializeDependencies()
preloadVolumeIcons()
registerReceiversAndCallbacks()

// 启动前台服务,并立即更新一次通知状态
Expand All @@ -131,6 +135,17 @@ class ForegroundService : Service() {
volumeComment = resources.getStringArray(R.array.array_volume_comment)
}

@SuppressLint("DiscouragedApi")
private fun preloadVolumeIcons() {
for (i in 0..100) {
val resourceName = "num_$i"
val resourceId = resources.getIdentifier(resourceName, "drawable", packageName)
if (resourceId != 0) {
volumeIconMap.put(i, resourceId)
}
}
}

@SuppressLint("UnspecifiedRegisterReceiverFlag")
private fun registerReceiversAndCallbacks() {
// 注册音频设备回调
Expand All @@ -139,14 +154,6 @@ class ForegroundService : Service() {
// 注册音量变化接收器
val volumeFilter = IntentFilter("android.media.VOLUME_CHANGED_ACTION")
registerReceiver(volumeChangeReceiver, volumeFilter)

// 注册休眠定时器更新接收器
val sleepFilter = IntentFilter(Constants.BROADCAST_ACTION_SLEEPTIMER_UPDATE)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
registerReceiver(sleepReceiver, sleepFilter, RECEIVER_NOT_EXPORTED)
} else {
registerReceiver(sleepReceiver, sleepFilter)
}
}

@SuppressLint("MissingPermission")
Expand All @@ -169,6 +176,10 @@ class ForegroundService : Service() {
Log.d(TAG, "Mute media action received.")
handleMuteMedia()
}
BROADCAST_ACTION_SLEEPTIMER_UPDATE -> {
Log.d(TAG, "Sleep timer update action received via onStartCommand.")
updateForegroundNotification()
}
Comment on lines +179 to +182

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The call to updateForegroundNotification() on line 181 is redundant because the same function is called unconditionally on line 186. This causes the notification to be updated twice for the BROADCAST_ACTION_SLEEPTIMER_UPDATE action, which is inefficient. You can safely remove the call from this block, as the one on line 186 will handle the update.

            BROADCAST_ACTION_SLEEPTIMER_UPDATE -> {
                Log.d(TAG, "Sleep timer update action received via onStartCommand.")
            }

}

// 确保服务被重新创建时,通知内容是最新的
Expand Down Expand Up @@ -205,7 +216,6 @@ class ForegroundService : Service() {

// 安全地反注册所有接收器和回调
safeUnregisterReceiver(volumeChangeReceiver)
safeUnregisterReceiver(sleepReceiver)
try {
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
} catch (e: Exception) {
Expand Down Expand Up @@ -540,12 +550,6 @@ class ForegroundService : Service() {
}
}

private val sleepReceiver = object : SleepReceiver() {
override fun updateNotification(context: Context) {
updateForegroundNotification()
}
}

// --- 通知创建 ---

private fun createForegroundNotification(context: Context): Notification {
Expand Down Expand Up @@ -619,10 +623,9 @@ class ForegroundService : Service() {
)
}

@SuppressLint("DiscouragedApi")
private fun generateNotificationIcon(context: Context, volumePercent: Int, volumeLevel: Int): IconCompat {
val resourceName = "num_${volumePercent}"
val resourceId = resources.getIdentifier(resourceName, "drawable", context.packageName)
// 直接从 SparseIntArray 中查找,速度极快
val resourceId = volumeIconMap.get(volumePercent, 0) // 第二个参数是找不到时的默认值

return if (resourceId != 0) {
IconCompat.createWithResource(this, resourceId)
Expand Down
10 changes: 5 additions & 5 deletions build.gradle
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
id 'com.android.application' version '8.11.1' apply false
id 'com.android.library' version '8.11.1' apply false
id 'org.jetbrains.kotlin.android' version '2.2.10' apply false
id 'com.google.devtools.ksp' version "2.2.10-2.0.2" apply false
id 'org.jetbrains.kotlin.plugin.compose' version '2.2.10' apply false
id 'com.android.application' version '8.11.2' apply false
id 'com.android.library' version '8.11.2' apply false
id 'org.jetbrains.kotlin.android' version '2.2.20' apply false
id 'com.google.devtools.ksp' version "2.2.20-2.0.3" apply false
id 'org.jetbrains.kotlin.plugin.compose' version '2.2.20' apply false
id 'com.google.dagger.hilt.android' version '2.57.1' apply false
}
Loading