mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-27 15:26:58 +00:00
Improve Live Information (Announcements) (#4722)
This commit is contained in:
@@ -26,11 +26,14 @@ import androidx.compose.material3.surfaceColorAtElevation
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
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.alpha
|
||||
import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
@@ -42,20 +45,28 @@ import app.lawnchair.ui.preferences.components.layout.PreferenceTemplate
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.liveInformationManager
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.model.Announcement
|
||||
import app.lawnchair.ui.util.addIf
|
||||
import com.android.launcher3.BuildConfig
|
||||
import com.android.launcher3.R
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun AnnouncementPreference() {
|
||||
val liveInformationManager = liveInformationManager()
|
||||
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val enabled by liveInformationManager.enabled.asState()
|
||||
val showAnnouncements by liveInformationManager.showAnnouncements.asState()
|
||||
val dismissedAnnouncementIds by liveInformationManager.dismissedAnnouncementIds.asState()
|
||||
val liveInformation by liveInformationManager.liveInformation.asState()
|
||||
|
||||
val announcements = remember { liveInformation.announcements.filter { it.id !in dismissedAnnouncementIds } }
|
||||
|
||||
if (enabled && showAnnouncements) {
|
||||
AnnouncementPreference(
|
||||
announcements = liveInformation.announcements,
|
||||
announcements = announcements,
|
||||
onDismiss = { announcement ->
|
||||
val dismissed = dismissedAnnouncementIds.toMutableSet().apply { add(announcement.id) }
|
||||
coroutineScope.launch { liveInformationManager.dismissedAnnouncementIds.set(dismissed) }
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -63,15 +74,21 @@ fun AnnouncementPreference() {
|
||||
@Composable
|
||||
fun AnnouncementPreference(
|
||||
announcements: List<Announcement>,
|
||||
onDismiss: (Announcement) -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier,
|
||||
) {
|
||||
announcements.forEachIndexed { index, announcement ->
|
||||
var show by rememberSaveable { mutableStateOf(true) }
|
||||
AnnouncementItem(show, announcement) { show = false }
|
||||
if (index != announcements.lastIndex && show && announcement.active && (!announcement.test || BuildConfig.DEBUG)) {
|
||||
var dismissed by rememberSaveable { mutableStateOf(false) }
|
||||
val visible = announcement.shouldBeVisible && !dismissed
|
||||
|
||||
AnnouncementItem(visible, announcement) {
|
||||
onDismiss(announcement)
|
||||
dismissed = true
|
||||
}
|
||||
if (index != announcements.lastIndex && visible) {
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
}
|
||||
}
|
||||
@@ -80,20 +97,19 @@ fun AnnouncementPreference(
|
||||
|
||||
@Composable
|
||||
private fun AnnouncementItem(
|
||||
show: Boolean,
|
||||
visible: Boolean,
|
||||
announcement: Announcement,
|
||||
modifier: Modifier = Modifier,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
ExpandAndShrink(
|
||||
modifier = modifier,
|
||||
visible = show && announcement.active &&
|
||||
announcement.text.isNotBlank() &&
|
||||
(!announcement.test || BuildConfig.DEBUG),
|
||||
visible = visible,
|
||||
) {
|
||||
AnnouncementItemContent(
|
||||
text = announcement.text,
|
||||
url = announcement.url,
|
||||
icon = announcement.getIcon(),
|
||||
onClose = onClose,
|
||||
)
|
||||
}
|
||||
@@ -103,6 +119,7 @@ private fun AnnouncementItem(
|
||||
private fun AnnouncementItemContent(
|
||||
text: String,
|
||||
url: String?,
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
onClose: () -> Unit,
|
||||
) {
|
||||
@@ -112,6 +129,7 @@ private fun AnnouncementItemContent(
|
||||
SwipeToDismissBoxValue.StartToEnd -> {
|
||||
onClose()
|
||||
}
|
||||
|
||||
SwipeToDismissBoxValue.EndToStart -> return@rememberSwipeToDismissBoxState false
|
||||
SwipeToDismissBoxValue.Settled -> return@rememberSwipeToDismissBoxState false
|
||||
}
|
||||
@@ -151,7 +169,11 @@ private fun AnnouncementItemContent(
|
||||
shape = MaterialTheme.shapes.large,
|
||||
color = MaterialTheme.colorScheme.surfaceVariant,
|
||||
) {
|
||||
AnnouncementPreferenceItemContent(text = text, url = url)
|
||||
AnnouncementPreferenceItemContent(
|
||||
text = text,
|
||||
url = url,
|
||||
icon = icon,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -167,6 +189,7 @@ private fun calculateAlpha(progress: Float): Float {
|
||||
private fun AnnouncementPreferenceItemContent(
|
||||
text: String,
|
||||
url: String?,
|
||||
icon: ImageVector,
|
||||
modifier: Modifier = Modifier,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
@@ -195,7 +218,7 @@ private fun AnnouncementPreferenceItemContent(
|
||||
},
|
||||
startWidget = {
|
||||
Icon(
|
||||
imageVector = Icons.Rounded.NewReleases,
|
||||
imageVector = icon,
|
||||
tint = MaterialTheme.colorScheme.primary,
|
||||
contentDescription = null,
|
||||
)
|
||||
@@ -225,6 +248,7 @@ private fun InfoPreferenceWithoutLinkPreview() {
|
||||
AnnouncementPreferenceItemContent(
|
||||
text = "Very important announcement ",
|
||||
url = "",
|
||||
icon = Icons.Rounded.NewReleases,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -234,5 +258,6 @@ private fun InfoPreferenceWithLinkPreview() {
|
||||
AnnouncementPreferenceItemContent(
|
||||
text = "Very important announcement with a very important link",
|
||||
url = "https://lawnchair.app/",
|
||||
icon = Icons.Rounded.NewReleases,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -6,10 +6,12 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.datastore.preferences.core.booleanPreferencesKey
|
||||
import androidx.datastore.preferences.core.stringPreferencesKey
|
||||
import androidx.datastore.preferences.preferencesDataStore
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.model.AnnouncementId
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.model.LiveInformation
|
||||
import com.android.launcher3.R
|
||||
import com.android.launcher3.util.MainThreadInitializedObject
|
||||
import com.patrykmichalik.opto.core.PreferenceManager
|
||||
import kotlinx.serialization.encodeToString
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
class LiveInformationManager private constructor(context: Context) : PreferenceManager {
|
||||
@@ -41,8 +43,28 @@ class LiveInformationManager private constructor(context: Context) : PreferenceM
|
||||
val liveInformation = preference(
|
||||
key = stringPreferencesKey(name = "live_information"),
|
||||
defaultValue = LiveInformation.default,
|
||||
parse = { Json.decodeFromString<LiveInformation>(it) },
|
||||
save = { Json.encodeToString(LiveInformation.serializer(), it) },
|
||||
parse = { string ->
|
||||
val withUnknownKeys = Json { ignoreUnknownKeys = true }
|
||||
withUnknownKeys.decodeFromString<LiveInformation>(string)
|
||||
},
|
||||
save = { liveInformation ->
|
||||
Json.encodeToString(
|
||||
LiveInformation.serializer(),
|
||||
liveInformation,
|
||||
)
|
||||
},
|
||||
)
|
||||
|
||||
val dismissedAnnouncementIds = preference(
|
||||
key = stringPreferencesKey(name = "dismissed_announcement_ids"),
|
||||
defaultValue = emptySet(),
|
||||
parse = {
|
||||
val withUnknownKeys = Json { ignoreUnknownKeys = true }
|
||||
withUnknownKeys.decodeFromString<Set<AnnouncementId>>(it)
|
||||
},
|
||||
save = {
|
||||
Json.encodeToString(it)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,60 @@
|
||||
package app.lawnchair.ui.preferences.data.liveinfo.model
|
||||
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.rounded.BugReport
|
||||
import androidx.compose.material.icons.rounded.CheckCircle
|
||||
import androidx.compose.material.icons.rounded.Error
|
||||
import androidx.compose.material.icons.rounded.Favorite
|
||||
import androidx.compose.material.icons.rounded.Feedback
|
||||
import androidx.compose.material.icons.rounded.Forum
|
||||
import androidx.compose.material.icons.rounded.Hub
|
||||
import androidx.compose.material.icons.rounded.Loyalty
|
||||
import androidx.compose.material.icons.rounded.NewReleases
|
||||
import androidx.compose.material.icons.rounded.PriorityHigh
|
||||
import androidx.compose.material.icons.rounded.PrivacyTip
|
||||
import androidx.compose.material.icons.rounded.Sos
|
||||
import androidx.compose.material.icons.rounded.Star
|
||||
import androidx.compose.material.icons.rounded.Support
|
||||
import androidx.compose.material.icons.rounded.Warning
|
||||
import com.android.launcher3.BuildConfig
|
||||
import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class Announcement(
|
||||
val text: String,
|
||||
val url: String? = null,
|
||||
val active: Boolean = true,
|
||||
val test: Boolean = false,
|
||||
)
|
||||
private val active: Boolean = true,
|
||||
private val test: Boolean = false,
|
||||
private val channel: String? = null,
|
||||
private val icon: String? = null,
|
||||
) {
|
||||
|
||||
val id: AnnouncementId get() = text to url
|
||||
|
||||
fun getIcon() = when (icon) {
|
||||
"bug-report" -> Icons.Rounded.BugReport
|
||||
"check-circle" -> Icons.Rounded.CheckCircle
|
||||
"error" -> Icons.Rounded.Error
|
||||
"favorite" -> Icons.Rounded.Favorite
|
||||
"feedback" -> Icons.Rounded.Feedback
|
||||
"forum" -> Icons.Rounded.Forum
|
||||
"hub" -> Icons.Rounded.Hub
|
||||
"loyalty" -> Icons.Rounded.Loyalty
|
||||
"priority-high" -> Icons.Rounded.PriorityHigh
|
||||
"privacy-tip" -> Icons.Rounded.PrivacyTip
|
||||
"sos" -> Icons.Rounded.Sos
|
||||
"star" -> Icons.Rounded.Star
|
||||
"support" -> Icons.Rounded.Support
|
||||
"warning" -> Icons.Rounded.Warning
|
||||
else -> Icons.Rounded.NewReleases
|
||||
}
|
||||
|
||||
val shouldBeVisible
|
||||
get(): Boolean {
|
||||
if (active.not()) return false
|
||||
if (text.isBlank()) return false
|
||||
if (test && BuildConfig.DEBUG.not()) return false
|
||||
if (channel != null && channel != BuildConfig.FLAVOR_channel) return false
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
package app.lawnchair.ui.preferences.data.liveinfo.model
|
||||
|
||||
typealias AnnouncementId = Pair<String, String?>
|
||||
@@ -4,12 +4,13 @@ import kotlinx.serialization.Serializable
|
||||
|
||||
@Serializable
|
||||
data class LiveInformation(
|
||||
private val version: Int = 1,
|
||||
private val version: Int = 2,
|
||||
val announcements: List<Announcement>,
|
||||
) {
|
||||
|
||||
companion object {
|
||||
val default = LiveInformation(
|
||||
version = 1,
|
||||
version = 2,
|
||||
announcements = emptyList(),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -18,9 +18,12 @@ import app.lawnchair.ui.preferences.components.controls.SwitchPreference
|
||||
import app.lawnchair.ui.preferences.components.controls.TextPreference
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceGroup
|
||||
import app.lawnchair.ui.preferences.components.layout.PreferenceLayout
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.liveInformationManager
|
||||
import app.lawnchair.ui.preferences.data.liveinfo.model.LiveInformation
|
||||
import com.android.launcher3.settings.SettingsActivity
|
||||
import com.android.launcher3.uioverrides.flags.DeveloperOptionsFragment
|
||||
import com.patrykmichalik.opto.domain.Preference
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
/**
|
||||
* A screen to house unfinished preferences and debug flags
|
||||
@@ -31,6 +34,7 @@ fun DebugMenuPreferences(
|
||||
) {
|
||||
val prefs = preferenceManager()
|
||||
val prefs2 = preferenceManager2()
|
||||
val liveInfoManager = liveInformationManager()
|
||||
val flags = remember { prefs.debugFlags }
|
||||
val flags2 = remember { prefs2.debugFlags }
|
||||
val textFlags = remember { prefs2.textFlags }
|
||||
@@ -60,6 +64,15 @@ fun DebugMenuPreferences(
|
||||
label = "Crash launcher",
|
||||
onClick = { throw RuntimeException("User triggered crash") },
|
||||
)
|
||||
ClickablePreference(
|
||||
label = "Reset live information",
|
||||
onClick = {
|
||||
runBlocking {
|
||||
liveInfoManager.liveInformation.set(LiveInformation.default)
|
||||
liveInfoManager.dismissedAnnouncementIds.set(emptySet())
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
PreferenceGroup(heading = "Debug flags") {
|
||||
|
||||
Reference in New Issue
Block a user