✨ Android featured post widget
This commit is contained in:
parent
abc21f858b
commit
00678c0ac8
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
)
|
@ -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)
|
||||
|
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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 {}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user