Improve Live Information (Announcements) (#4722)

This commit is contained in:
Yasan
2024-09-14 11:29:39 +02:00
committed by GitHub
parent d2d917cebd
commit c1108aebdf
6 changed files with 131 additions and 18 deletions

View File

@@ -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,
)
}

View File

@@ -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)
},
)
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,3 @@
package app.lawnchair.ui.preferences.data.liveinfo.model
typealias AnnouncementId = Pair<String, String?>

View File

@@ -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(),
)
}

View File

@@ -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") {