Android featured post widget

This commit is contained in:
LittleSheep 2024-12-15 18:57:54 +08:00
parent abc21f858b
commit 00678c0ac8
6 changed files with 177 additions and 12 deletions

View File

@ -14,6 +14,8 @@ dependencies {
implementation "androidx.glance:glance-appwidget:1.1.1"
implementation 'androidx.compose.foundation:foundation-layout-android:1.7.6'
implementation 'com.google.code.gson:gson:2.10.1'
implementation 'io.coil-kt.coil3:coil-compose:3.0.4'
implementation 'io.coil-kt.coil3:coil-network-okhttp:3.0.4'
}
android {

View File

@ -52,12 +52,22 @@
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="*/*" />
<data android:mimeType="image/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<intent-filter>
<action android:name="android.intent.action.SEND_MULTIPLE" />
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="video/*" />
</intent-filter>
<!-- Specifies an Android theme to apply to this Activity as soon as

View File

@ -0,0 +1,32 @@
package dev.solsynth.solian.data
import java.time.Instant
data class SolarPost(
val id: Int,
val body: SolarPostBody,
val publisher: SolarPublisher,
val publisherId: Int,
val createdAt: Instant,
val updatedAt: Instant,
val editedAt: Instant?,
val publishedAt: Instant?
)
data class SolarPostBody(
val content: String?,
val title: String?,
val description: String?,
val attachments: List<String>?
)
data class SolarPublisher(
val id: Int,
val name: String,
val nick: String,
val description: String?,
val avatar: String?,
val banner: String?,
val createdAt: Instant,
val updatedAt: Instant
)

View File

@ -11,7 +11,6 @@ import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.currentState
import androidx.glance.layout.Alignment
import androidx.glance.layout.Box
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
@ -49,7 +48,7 @@ class CheckInWidget : GlanceAppWidget() {
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Instant::class.java, InstantAdapter())
.create()
val resultTierSymbols = listOf("大凶", "", "中平", "", "")
val resultTierSymbols = listOf("大凶", "", "中平", "", "")
val prefs = currentState.preferences
val checkInRaw = prefs.getString("today_check_in", null)

View File

@ -1,17 +1,42 @@
import android.content.Context
import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.glance.GlanceId
import androidx.glance.GlanceModifier
import androidx.glance.appwidget.GlanceAppWidget
import androidx.glance.appwidget.provideContent
import androidx.glance.background
import androidx.glance.currentState
import androidx.glance.layout.Box
import androidx.glance.layout.Alignment
import androidx.glance.layout.Column
import androidx.glance.layout.Row
import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.height
import androidx.glance.layout.padding
import androidx.glance.layout.width
import androidx.glance.state.GlanceStateDefinition
import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight
import androidx.glance.text.Text
import androidx.glance.text.TextStyle
import coil3.ImageLoader
import coil3.compose.AsyncImage
import coil3.compose.setSingletonImageLoaderFactory
import coil3.request.crossfade
import com.google.gson.FieldNamingPolicy
import com.google.gson.GsonBuilder
import dev.solsynth.solian.data.InstantAdapter
import dev.solsynth.solian.data.SolarPost
import java.time.Instant
import java.time.LocalDateTime
import java.time.ZoneId
import java.time.format.DateTimeFormatter
class FeaturedPostWidget : GlanceAppWidget() {
override val stateDefinition: GlanceStateDefinition<*>?
@ -23,15 +48,112 @@ class FeaturedPostWidget : GlanceAppWidget() {
}
}
private val serverUrl = "https://api.sn.solsynth.dev"
private fun getAttachmentUrl(identifier: String): String {
return if (identifier.startsWith("http")) {
identifier
} else {
"$serverUrl/cgi/uc/attachments/$identifier"
}
}
@Composable
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(true)
.build()
}
val gson =
GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
.registerTypeAdapter(Instant::class.java, InstantAdapter())
.create()
val prefs = currentState.preferences
val checkIn = prefs.getString("post_featured", null)
Box(modifier = GlanceModifier.background(Color.White).padding(16.dp)) {
checkIn?.let {
Text(it)
} ?: run {
Text("No featured posts")
val postFeaturedRaw = prefs.getString("post_featured", null)
Column(
modifier = GlanceModifier
.fillMaxWidth()
.background(Color.White)
.padding(16.dp)
) {
if (postFeaturedRaw != null) {
val postFeaturedList: Array<SolarPost> =
gson.fromJson(postFeaturedRaw, Array<SolarPost>::class.java)
val postFeatured = postFeaturedList.firstOrNull();
Row {
Text(
text = postFeatured?.publisher?.nick ?: "Unknown",
style = TextStyle(fontSize = 15.sp)
)
Spacer(modifier = GlanceModifier.width(8.dp))
Text(
text = "@${postFeatured?.publisher?.name}",
style = TextStyle(fontSize = 13.sp, fontFamily = FontFamily.Monospace)
)
}
Spacer(modifier = GlanceModifier.height(8.dp))
if (postFeatured?.body?.title != null) {
Text(
text = postFeatured.body.title,
style = TextStyle(fontSize = 25.sp, fontFamily = FontFamily.Serif)
)
}
if (postFeatured?.body?.description != null) {
Text(
text = postFeatured.body.description,
style = TextStyle(fontSize = 19.sp, fontFamily = FontFamily.Serif)
)
}
if (postFeatured?.body?.title != null || postFeatured?.body?.description != null) {
Spacer(modifier = GlanceModifier.height(8.dp))
}
Text(
text = postFeatured?.body?.content ?: "No content",
style = TextStyle(fontSize = 15.sp),
)
Spacer(modifier = GlanceModifier.height(8.dp))
if (postFeatured?.createdAt != null) {
Text(
LocalDateTime.ofInstant(postFeatured.createdAt, ZoneId.systemDefault())
.format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm")),
style = TextStyle(fontSize = 13.sp),
)
}
Text(
"Solar Network Featured Post",
style = TextStyle(fontSize = 11.sp, fontWeight = FontWeight.Bold),
)
return@Column;
}
Column(
modifier = GlanceModifier.fillMaxSize(),
verticalAlignment = Alignment.Vertical.CenterVertically,
horizontalAlignment = Alignment.Horizontal.CenterHorizontally
) {
Text(
text = "No featured posts",
style = TextStyle(fontSize = 17.sp, fontWeight = FontWeight.Bold)
)
Text(
text = "Open the app to load recommendations",
style = TextStyle(fontSize = 15.sp)
)
}
}
}

View File

@ -60,7 +60,7 @@ struct CheckInEntry: TimelineEntry {
struct CheckInWidgetEntryView : View {
var entry: CheckInProvider.Entry
private let resultTierSymbols: [String] = ["大凶", "", "中平", "", ""]
private let resultTierSymbols: [String] = ["大凶", "", "中平", "", ""]
func checkIn() -> Void {}