diff --git a/app/build.gradle b/app/build.gradle
index 131a3bd..15fd828 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -2,7 +2,7 @@ plugins {
id 'com.android.application'
id 'org.jetbrains.kotlin.android'
id 'kotlin-android'
- id 'kotlin-kapt'
+ id 'com.google.devtools.ksp'
}
def keystorePropertiesFile = rootProject.file("key.properties")
@@ -18,14 +18,14 @@ android {
applicationId "com.maary.liveinpeace"
minSdk 31
targetSdk 34
- versionCode 4
- versionName "2.1_beta"
+ versionCode 5
+ versionName "2.2_beta"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
}
buildFeatures {
viewBinding true
- dataBinding true
+ dataBinding false
}
splits {
// Configures multiple APKs based on ABI.
@@ -85,16 +85,16 @@ android {
dependencies {
- implementation 'androidx.core:core-ktx:1.10.1'
+ implementation 'androidx.core:core-ktx:1.13.1'
implementation 'androidx.appcompat:appcompat:1.6.1'
- implementation 'com.google.android.material:material:1.9.0'
+ implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
- implementation "androidx.activity:activity-ktx:1.7.2"
- implementation 'androidx.databinding:databinding-runtime:8.0.2'
+ implementation 'androidx.activity:activity-ktx:1.9.0'
+ implementation 'androidx.databinding:databinding-runtime:8.4.1'
- def lifecycle_version = "2.6.1"
+ def lifecycle_version = '2.8.0'
// LiveData
implementation "androidx.lifecycle:lifecycle-livedata-ktx:$lifecycle_version"
@@ -103,12 +103,12 @@ dependencies {
implementation "androidx.lifecycle:lifecycle-viewmodel-savedstate:$lifecycle_version"
- def room_version = "2.6.0-alpha01"
+ def room_version = '2.6.1'
implementation "androidx.room:room-runtime:$room_version"
annotationProcessor "androidx.room:room-compiler:$room_version"
- implementation "androidx.room:room-ktx:2.5.1"
- kapt "androidx.room:room-compiler:$room_version"
+ implementation 'androidx.room:room-ktx:2.6.1'
+ ksp "androidx.room:room-compiler:$room_version"
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index b9f10f1..4c04858 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -41,6 +41,16 @@
+
+
+
+
+
+
@@ -62,6 +72,11 @@
android:exported="false">
+
+
+
+
+
diff --git a/app/src/main/java/com/maary/liveinpeace/Constants.kt b/app/src/main/java/com/maary/liveinpeace/Constants.kt
index 0974f07..67c7a22 100644
--- a/app/src/main/java/com/maary/liveinpeace/Constants.kt
+++ b/app/src/main/java/com/maary/liveinpeace/Constants.kt
@@ -23,6 +23,7 @@ class Constants {
const val ID_NOTIFICATION_ALERT = 2
const val ID_NOTIFICATION_PROTECT = 4
const val ID_NOTIFICATION_WELCOME = 0
+ const val ID_NOTIFICATION_SLEEPTIMER = 5
// 设置图像式图标 Action
const val ACTION_NAME_SET_IMG = "com.maary.liveinpeace.receiver.SettingsReceiver.SetIconImg"
// 设置字符式图标 Action
@@ -37,6 +38,11 @@ class Constants {
const val ACTION_NAME_SETTINGS = "com.maary.liveinpeace.receiver.SettingsReceiver"
// 静音广播名称
const val BROADCAST_ACTION_MUTE = "com.maary.liveinpeace.MUTE_MEDIA"
+ const val BROADCAST_ACTION_SLEEPTIMER_CANCEL = "com.maary.liveinpeace.action.CANCEL"
+ const val BROADCAST_ACTION_SLEEPTIMER_INCREMENT = "com.maary.liveinpeace.action.INCREMENT"
+ const val BROADCAST_ACTION_SLEEPTIMER_DECREMENT = "com.maary.liveinpeace.action.DECREMENT"
+ const val BROADCAST_ACTION_SLEEPTIMER_TOGGLE = "com.maary.liveinpeace.sleeptimer.TOGGLE"
+ const val BROADCAST_ACTION_SLEEPTIMER_UPDATE = "com.maary.liveinpeace.sleeptimer.UPDATE"
// 前台服务状态改变广播
const val BROADCAST_ACTION_FOREGROUND = "com.maary.liveinpeace.ACTION_FOREGROUND_SERVICE_STATE"
const val BROADCAST_FOREGROUND_INTENT_EXTRA = "isForegroundServiceRunning"
@@ -48,6 +54,7 @@ class Constants {
const val CHANNEL_ID_ALERT = "LIP_ALERT"
const val CHANNEL_ID_PROTECT = "LIP_PROTECT"
const val CHANNEL_ID_WELCOME = "LIP_WELCOME"
+ const val CHANNEL_ID_SLEEPTIMER = "LIP_SLEEPTIMER"
// 提醒时间
const val ALERT_TIME: Long = 2*60*60*1000
// 延后时间
@@ -57,6 +64,7 @@ class Constants {
const val ID_NOTIFICATION_GROUP_SETTINGS = "LIP_notification_group_settings"
const val ID_NOTIFICATION_GROUP_ALERTS = "LIP_notification_group_alerts"
const val ID_NOTIFICATION_GROUP_PROTECT = "LIP_notification_group_protect"
+ const val ID_NOTIFICATION_GROUP_SLEEPTIMER = "LIP_notification_group_sleeptimer"
const val PATTERN_DATE_DATABASE = "yyyy-MM-dd"
const val PATTERN_DATE_BUTTON = "MM/dd"
}
diff --git a/app/src/main/java/com/maary/liveinpeace/HistoryActivity.kt b/app/src/main/java/com/maary/liveinpeace/HistoryActivity.kt
index e9ec9ee..19043be 100644
--- a/app/src/main/java/com/maary/liveinpeace/HistoryActivity.kt
+++ b/app/src/main/java/com/maary/liveinpeace/HistoryActivity.kt
@@ -5,7 +5,6 @@ import android.view.View
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.core.view.WindowCompat
-import androidx.databinding.DataBindingUtil
import androidx.recyclerview.widget.LinearLayoutManager
import com.google.android.material.datepicker.CalendarConstraints
import com.google.android.material.datepicker.DateValidatorPointBackward
@@ -61,7 +60,7 @@ class HistoryActivity : AppCompatActivity(), DeviceMapChangeListener {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
WindowCompat.setDecorFitsSystemWindows(window, false)
- binding = DataBindingUtil.setContentView(this, R.layout.activity_history)
+ binding = ActivityHistoryBinding.inflate(layoutInflater)
setContentView(binding.root)
var pickedDate : String = LocalDate.now().toString()
diff --git a/app/src/main/java/com/maary/liveinpeace/SleepNotification.kt b/app/src/main/java/com/maary/liveinpeace/SleepNotification.kt
new file mode 100644
index 0000000..53e2384
--- /dev/null
+++ b/app/src/main/java/com/maary/liveinpeace/SleepNotification.kt
@@ -0,0 +1,103 @@
+package com.maary.liveinpeace
+
+import android.app.Notification
+import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
+import android.content.Intent
+import android.graphics.drawable.Icon
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_MUTE
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_CANCEL
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_DECREMENT
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_INCREMENT
+import java.text.DateFormat
+import java.util.Date
+import java.util.concurrent.TimeUnit
+import com.maary.liveinpeace.SleepNotification.Action.CANCEL
+import com.maary.liveinpeace.SleepNotification.Action.INCREMENT
+import com.maary.liveinpeace.SleepNotification.Action.DECREMENT
+import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_SLEEPTIMER
+import com.maary.liveinpeace.Constants.Companion.ID_NOTIFICATION_GROUP_SLEEPTIMER
+import com.maary.liveinpeace.Constants.Companion.ID_NOTIFICATION_SLEEPTIMER
+import com.maary.liveinpeace.receiver.MuteMediaReceiver
+
+
+object SleepNotification {
+
+ private val TIMEOUT_INITIAL_MILLIS = TimeUnit.MINUTES.toMillis(30)
+ private val TIMEOUT_INCREMENT_MILLIS = TimeUnit.MINUTES.toMillis(10)
+ private val TIMEOUT_DECREMENT_MILLIS = TimeUnit.MINUTES.toMillis(10)
+
+ private enum class Action(private val value: String) {
+ CANCEL(BROADCAST_ACTION_SLEEPTIMER_CANCEL) {
+ override fun title(context: Context) = context.getText(android.R.string.cancel)
+ },
+ INCREMENT(BROADCAST_ACTION_SLEEPTIMER_INCREMENT) {
+ override fun title(context: Context) = "+" + TimeUnit.MILLISECONDS.toMinutes(TIMEOUT_INCREMENT_MILLIS)
+ },
+ DECREMENT(BROADCAST_ACTION_SLEEPTIMER_DECREMENT) {
+ override fun title(context: Context) = "-" + TimeUnit.MILLISECONDS.toMinutes(TIMEOUT_DECREMENT_MILLIS)
+ },
+ ;
+
+ companion object {
+ fun parse(value: String?): Action? = entries.firstOrNull { it.value == value }
+ }
+
+ fun intent(context: Context): Intent = Intent(context, MuteMediaReceiver::class.java).setAction(value)
+
+ fun pendingIntent(context: Context, cancel: Boolean = false): PendingIntent? =
+ PendingIntent.getBroadcast(context, 0, intent(context), PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT ).apply { if (cancel) cancel() }
+
+ fun action(context: Context, cancel: Boolean = false): Notification.Action.Builder =
+ Notification.Action.Builder(Icon.createWithResource(context, 0), title(context), pendingIntent(context, cancel))
+
+ abstract fun title(context: Context): CharSequence?
+ }
+
+ fun Context.notificationManager(): NotificationManager? = getSystemService(NotificationManager::class.java)
+
+ fun Context.find() = notificationManager()?.activeNotifications?.firstOrNull { it.id == ID_NOTIFICATION_SLEEPTIMER }?.notification
+
+ fun Context.handle(intent: Intent?) = when (Action.parse(intent?.action)) {
+ INCREMENT -> update(TIMEOUT_INCREMENT_MILLIS)
+ DECREMENT -> update(-TIMEOUT_DECREMENT_MILLIS)
+ CANCEL -> cancel()
+ null -> Unit
+ }
+
+ fun Context.toggle() = if (find() == null) show() else cancel()
+
+ private fun Context.cancel() = notificationManager()?.cancel(ID_NOTIFICATION_SLEEPTIMER) ?: Unit
+
+ private fun Context.update(timeout: Long) = find()?.let { it.`when` - System.currentTimeMillis() }?.let { if (it > -timeout) it + timeout else it }?.let { show(it) }
+
+ private fun Context.show(timeout: Long = TIMEOUT_INITIAL_MILLIS) {
+ require(timeout > 0)
+ val eta = System.currentTimeMillis() + timeout
+
+ val muteMediaIntent = Intent(this, MuteMediaReceiver::class.java)
+ muteMediaIntent.action = BROADCAST_ACTION_MUTE
+ val pendingMuteIntent = PendingIntent.getBroadcast(this, 0, muteMediaIntent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+
+
+ val notification = Notification.Builder(this, CHANNEL_ID_SLEEPTIMER)
+ .setCategory(Notification.CATEGORY_EVENT)
+ .setVisibility(Notification.VISIBILITY_PUBLIC)
+ .setOnlyAlertOnce(true)
+ .setOngoing(true)
+ .setSmallIcon(R.drawable.ic_tile)
+ .setSubText(DateFormat.getTimeInstance(DateFormat.SHORT).format(Date(eta)))
+ .setShowWhen(true).setWhen(eta)
+ .setGroup(ID_NOTIFICATION_GROUP_SLEEPTIMER)
+ .setUsesChronometer(true).setChronometerCountDown(true)
+ .setTimeoutAfter(timeout)
+ .setDeleteIntent(pendingMuteIntent)
+ .addAction(INCREMENT.action(this).build())
+ .addAction(DECREMENT.action(this, cancel = timeout <= TIMEOUT_DECREMENT_MILLIS).build())
+ .addAction(CANCEL.action(this).build())
+ .build()
+ notificationManager()?.notify(ID_NOTIFICATION_SLEEPTIMER, notification)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/maary/liveinpeace/receiver/MuteMediaReceiver.kt b/app/src/main/java/com/maary/liveinpeace/receiver/MuteMediaReceiver.kt
index 91effb2..d80cb32 100644
--- a/app/src/main/java/com/maary/liveinpeace/receiver/MuteMediaReceiver.kt
+++ b/app/src/main/java/com/maary/liveinpeace/receiver/MuteMediaReceiver.kt
@@ -4,15 +4,36 @@ import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.media.AudioManager
-import com.maary.liveinpeace.Constants
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_MUTE
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_CANCEL
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_DECREMENT
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_INCREMENT
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_TOGGLE
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_UPDATE
+import com.maary.liveinpeace.SleepNotification.handle
+import com.maary.liveinpeace.SleepNotification.toggle
class MuteMediaReceiver: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
- if (p1?.action == Constants.BROADCAST_ACTION_MUTE){
+ if (p1?.action == BROADCAST_ACTION_MUTE){
val audioManager = p0?.getSystemService(Context.AUDIO_SERVICE) as AudioManager
do {
audioManager.adjustStreamVolume(AudioManager.STREAM_MUSIC, AudioManager.ADJUST_LOWER, 0)
} while (audioManager.getStreamVolume(AudioManager.STREAM_MUSIC) > 0)
}
+
+ 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)
+ }
+
+ if (p1?.action == BROADCAST_ACTION_SLEEPTIMER_TOGGLE) {
+ p0?.toggle()
+ val intent = Intent(BROADCAST_ACTION_SLEEPTIMER_UPDATE)
+ p0?.sendBroadcast(intent)
+ }
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/maary/liveinpeace/receiver/SleepReceiver.kt b/app/src/main/java/com/maary/liveinpeace/receiver/SleepReceiver.kt
new file mode 100644
index 0000000..cab5619
--- /dev/null
+++ b/app/src/main/java/com/maary/liveinpeace/receiver/SleepReceiver.kt
@@ -0,0 +1,18 @@
+package com.maary.liveinpeace.receiver
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_UPDATE
+
+abstract class SleepReceiver: BroadcastReceiver() {
+
+ override fun onReceive(context: Context, intent: Intent) {
+ if (intent.action == BROADCAST_ACTION_SLEEPTIMER_UPDATE
+ ) {
+ updateNotification(context)
+ }
+ }
+
+ abstract fun updateNotification(context: Context)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/maary/liveinpeace/service/ForegroundService.kt b/app/src/main/java/com/maary/liveinpeace/service/ForegroundService.kt
index a1eca08..d7b6574 100644
--- a/app/src/main/java/com/maary/liveinpeace/service/ForegroundService.kt
+++ b/app/src/main/java/com/maary/liveinpeace/service/ForegroundService.kt
@@ -16,8 +16,10 @@ import android.graphics.Rect
import android.media.AudioDeviceCallback
import android.media.AudioDeviceInfo
import android.media.AudioManager
+import android.os.Build
import android.os.IBinder
import android.util.Log
+import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.app.NotificationManagerCompat
import androidx.core.graphics.drawable.IconCompat
@@ -26,6 +28,8 @@ import com.maary.liveinpeace.Constants.Companion.ACTION_TOGGLE_AUTO_CONNECTION_A
import com.maary.liveinpeace.Constants.Companion.ALERT_TIME
import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_FOREGROUND
import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_MUTE
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_TOGGLE
+import com.maary.liveinpeace.Constants.Companion.BROADCAST_ACTION_SLEEPTIMER_UPDATE
import com.maary.liveinpeace.Constants.Companion.BROADCAST_FOREGROUND_INTENT_EXTRA
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_DEFAULT
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_PROTECT
@@ -45,16 +49,20 @@ import com.maary.liveinpeace.DeviceMapChangeListener
import com.maary.liveinpeace.DeviceTimer
import com.maary.liveinpeace.HistoryActivity
import com.maary.liveinpeace.R
+import com.maary.liveinpeace.SleepNotification.find
import com.maary.liveinpeace.database.Connection
import com.maary.liveinpeace.database.ConnectionDao
import com.maary.liveinpeace.database.ConnectionRoomDatabase
import com.maary.liveinpeace.receiver.MuteMediaReceiver
import com.maary.liveinpeace.receiver.SettingsReceiver
+import com.maary.liveinpeace.receiver.SleepReceiver
import com.maary.liveinpeace.receiver.VolumeReceiver
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
+import java.text.DateFormat
import java.time.LocalDate
+import java.util.Date
class ForegroundService: Service() {
@@ -134,6 +142,15 @@ class ForegroundService: Service() {
}
}
+ private val sleepReceiver = object : SleepReceiver() {
+ @SuppressLint("MissingPermission")
+ override fun updateNotification(context: Context) {
+ with(NotificationManagerCompat.from(applicationContext)){
+ notify(ID_NOTIFICATION_FOREGROUND, createForegroundNotification(applicationContext))
+ } }
+
+ }
+
private fun saveDataWhenStop(){
val disconnectedTime = System.currentTimeMillis()
@@ -287,6 +304,7 @@ class ForegroundService: Service() {
}
}
+ @RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onCreate() {
super.onCreate()
@@ -301,6 +319,11 @@ class ForegroundService: Service() {
}
registerReceiver(volumeChangeReceiver, filter)
+ val sleepFilter = IntentFilter().apply {
+ addAction(BROADCAST_ACTION_SLEEPTIMER_UPDATE)
+ }
+ registerReceiver(sleepReceiver, sleepFilter, RECEIVER_NOT_EXPORTED)
+
database = ConnectionRoomDatabase.getDatabase(applicationContext)
connectionDao = database.connectionDao()
startForeground(ID_NOTIFICATION_FOREGROUND, createForegroundNotification(context = applicationContext))
@@ -323,11 +346,11 @@ class ForegroundService: Service() {
saveDataWhenStop()
// 取消注册音量变化广播接收器
unregisterReceiver(volumeChangeReceiver)
+ unregisterReceiver(sleepReceiver)
audioManager.unregisterAudioDeviceCallback(audioDeviceCallback)
val notificationManager: NotificationManager =
getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
notificationManager.cancel(ID_NOTIFICATION_FOREGROUND)
-// stopForeground(STOP_FOREGROUND_REMOVE)
Log.v("MUTE_TEST", "ON_DESTROY_FINISH")
super.onDestroy()
}
@@ -387,15 +410,29 @@ class ForegroundService: Service() {
protectionPendingIntent
).build()
- val historyIntent = Intent(this, HistoryActivity::class.java)
- historyIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
-
- val pendingHistoryIntent = PendingIntent.getActivity(context, 0, historyIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
-
- val actionHistory: NotificationCompat.Action = NotificationCompat.Action.Builder(
- R.drawable.ic_action_history,
- resources.getString(R.string.history),
- pendingHistoryIntent
+// val historyIntent = Intent(this, HistoryActivity::class.java)
+// historyIntent.flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK
+//
+// val pendingHistoryIntent = PendingIntent.getActivity(context, 0, historyIntent, PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+//
+// val actionHistory: NotificationCompat.Action = NotificationCompat.Action.Builder(
+// R.drawable.ic_action_history,
+// resources.getString(R.string.history),
+// pendingHistoryIntent
+// ).build()
+
+ val sleepIntent = Intent(context, MuteMediaReceiver::class.java)
+ sleepIntent.action = BROADCAST_ACTION_SLEEPTIMER_TOGGLE
+ val pendingSleepIntent = PendingIntent.getBroadcast(context, 0, sleepIntent, PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT)
+ val sleepNotification = find()
+ var sleepTitle = resources.getString(R.string.sleep)
+ if (sleepNotification != null ){
+ sleepTitle = DateFormat.getTimeInstance(DateFormat.SHORT).format(Date(sleepNotification.`when`))
+ }
+ val actionSleepTimer: NotificationCompat.Action = NotificationCompat.Action.Builder (
+ R.drawable.ic_tile,
+ sleepTitle,
+ pendingSleepIntent
).build()
val muteMediaIntent = Intent(context, MuteMediaReceiver::class.java)
@@ -415,7 +452,8 @@ class ForegroundService: Service() {
.setContentIntent(pendingMuteIntent)
.setPriority(NotificationCompat.PRIORITY_LOW)
.addAction(actionSettings)
- .addAction(actionHistory)
+// .addAction(actionHistory)
+ .addAction(actionSleepTimer)
.addAction(actionProtection)
.setGroup(ID_NOTIFICATION_GROUP_FORE)
.setGroupSummary(false)
diff --git a/app/src/main/java/com/maary/liveinpeace/service/HistoryTileService.kt b/app/src/main/java/com/maary/liveinpeace/service/HistoryTileService.kt
new file mode 100644
index 0000000..df92bda
--- /dev/null
+++ b/app/src/main/java/com/maary/liveinpeace/service/HistoryTileService.kt
@@ -0,0 +1,31 @@
+package com.maary.liveinpeace.service
+
+import android.app.PendingIntent
+import android.content.Intent
+import android.content.Intent.FLAG_ACTIVITY_NEW_TASK
+import android.os.Build
+import android.service.quicksettings.TileService
+import com.maary.liveinpeace.HistoryActivity
+
+class HistoryTileService: TileService() {
+ override fun onClick() {
+ super.onClick()
+
+ val intent = Intent(this, HistoryActivity::class.java)
+ .addFlags(FLAG_ACTIVITY_NEW_TASK)
+
+ val pendingIntent = PendingIntent.getActivity(
+ this,
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or
+ PendingIntent.FLAG_IMMUTABLE)
+
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE){
+ startActivityAndCollapse(pendingIntent)
+ }else {
+ startActivityAndCollapse(intent)
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/maary/liveinpeace/service/QSTileService.kt b/app/src/main/java/com/maary/liveinpeace/service/QSTileService.kt
index 7fe9171..357eeac 100644
--- a/app/src/main/java/com/maary/liveinpeace/service/QSTileService.kt
+++ b/app/src/main/java/com/maary/liveinpeace/service/QSTileService.kt
@@ -29,6 +29,7 @@ import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_ALERT
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_DEFAULT
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_PROTECT
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_SETTINGS
+import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_SLEEPTIMER
import com.maary.liveinpeace.Constants.Companion.CHANNEL_ID_WELCOME
import com.maary.liveinpeace.Constants.Companion.ID_NOTIFICATION_GROUP_SETTINGS
import com.maary.liveinpeace.Constants.Companion.ID_NOTIFICATION_SETTINGS
@@ -92,6 +93,15 @@ class QSTileService: TileService() {
)
}
+ if (notificationManager.getNotificationChannel(CHANNEL_ID_SLEEPTIMER) == null){
+ createNotificationChannel(
+ NotificationManager.IMPORTANCE_MIN,
+ CHANNEL_ID_SLEEPTIMER,
+ resources.getString(R.string.sleeptimer_channel),
+ resources.getString(R.string.sleeptimer_channel_description)
+ )
+ }
+
val powerManager = getSystemService(Context.POWER_SERVICE) as PowerManager
while(ActivityCompat.checkSelfPermission(
diff --git a/app/src/main/res/drawable/ic_tile.xml b/app/src/main/res/drawable/ic_tile.xml
new file mode 100644
index 0000000..835032e
--- /dev/null
+++ b/app/src/main/res/drawable/ic_tile.xml
@@ -0,0 +1,18 @@
+
+
+
+
+
diff --git a/app/src/main/res/layout/activity_history.xml b/app/src/main/res/layout/activity_history.xml
index 2fa8e22..86219f4 100644
--- a/app/src/main/res/layout/activity_history.xml
+++ b/app/src/main/res/layout/activity_history.xml
@@ -1,105 +1,105 @@
-
-
+
+
+ android:layout_height="wrap_content"
+ android:background="?attr/colorSurfaceContainerHighest"
+ android:fitsSystemWindows="true"
+ app:liftOnScroll="true">
-
+ android:layout_height="?attr/collapsingToolbarLayoutLargeSize"
+ app:contentScrim="?attr/colorSurfaceContainerHighest"
+ app:layout_scrollFlags="scroll|exitUntilCollapsed|snap">
-
+
-
+
-
+
-
+ android:background="@android:color/transparent"
+ app:layout_constraintStart_toStartOf="parent"
+ app:layout_constraintTop_toTopOf="parent"
+ app:selectionRequired="true"
+ app:singleSelection="true">
-
-
-
-
-
-
+ android:text="@string/timeline" />
+ android:text="@string/summary" />
+
+
+
+
-
+
-
+
-
+
+ android:background="@android:color/transparent"
+ android:orientation="vertical">
+ android:text="@string/current_connections"
+ android:textSize="16sp"
+ android:textStyle="bold" />
+ tools:itemCount="2"
+ tools:listitem="@layout/item_current_connections" />
+ android:text="@string/connections_history"
+ android:textSize="16sp"
+ android:textStyle="bold" />
+ tools:listitem="@layout/item_connection" />
-
-
-
-
-
-
-
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 2fe72c7..fe6c5a5 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -49,4 +49,7 @@
LiveInPeace 需要这些权限
电池
通知
+ 睡眠定时器
+ 用于睡眠定时器的通知
+ 睡觉!
\ 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 8b5b861..4cb13ab 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -41,6 +41,9 @@
LiveInPeace needs permissions to work.
Battery
Notification
+ Sleep Timer
+ Sleep Timer Notification
+ Sleep!
- Shhhhhhh…
- Perfectly Balanced, As All Things Should Be…
diff --git a/build.gradle b/build.gradle
index 8d7c329..147c328 100644
--- a/build.gradle
+++ b/build.gradle
@@ -1,6 +1,7 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
plugins {
- id 'com.android.application' version '8.3.1' apply false
- id 'com.android.library' version '8.3.1' apply false
- id 'org.jetbrains.kotlin.android' version '1.9.0-Beta' apply false
+ id 'com.android.application' version '8.4.0' apply false
+ id 'com.android.library' version '8.4.0' apply false
+ id 'org.jetbrains.kotlin.android' version '2.0.0' apply false
+ id 'com.google.devtools.ksp' version "2.0.0-1.0.21" apply false
}
\ No newline at end of file
diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties
index 7a55c6d..647c950 100644
--- a/gradle/wrapper/gradle-wrapper.properties
+++ b/gradle/wrapper/gradle-wrapper.properties
@@ -1,6 +1,6 @@
#Fri May 12 10:24:55 CST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
-distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
+distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists