🐛 Fix android widget don't work fine in release mode

This commit is contained in:
LittleSheep 2024-12-15 21:27:49 +08:00
parent 2eb1f4b52b
commit 7508a54907
10 changed files with 80 additions and 52 deletions

View File

@ -49,10 +49,18 @@ android {
} }
buildTypes { buildTypes {
debug {
minifyEnabled true
shrinkResources true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
release { release {
// TODO: Add your own signing config for the release build. // TODO: Add your own signing config for the release build.
// Signing with the debug keys for now, so `flutter run --release` works. // Signing with the debug keys for now, so `flutter run --release` works.
signingConfig = signingConfigs.debug signingConfig = signingConfigs.debug
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
} }
} }
} }

View File

@ -1,7 +1,9 @@
package dev.solsynth.solian.data package dev.solsynth.solian.data
import androidx.annotation.Keep
import java.time.Instant import java.time.Instant
@Keep
data class SolarPost( data class SolarPost(
val id: Int, val id: Int,
val body: SolarPostBody, val body: SolarPostBody,
@ -13,13 +15,14 @@ data class SolarPost(
val publishedAt: Instant? val publishedAt: Instant?
) )
@Keep
data class SolarPostBody( data class SolarPostBody(
val content: String?, val content: String?,
val title: String?, val title: String?,
val description: String?, val description: String?,
val attachments: List<String>?
) )
@Keep
data class SolarPublisher( data class SolarPublisher(
val id: Int, val id: Int,
val name: String, val name: String,

View File

@ -1,5 +1,6 @@
package dev.solsynth.solian.data package dev.solsynth.solian.data
import androidx.annotation.Keep
import com.google.gson.JsonDeserializationContext import com.google.gson.JsonDeserializationContext
import com.google.gson.JsonDeserializer import com.google.gson.JsonDeserializer
import com.google.gson.JsonElement import com.google.gson.JsonElement
@ -11,7 +12,7 @@ import java.lang.reflect.Type
import java.time.Instant import java.time.Instant
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
@Keep
class InstantAdapter : JsonSerializer<Instant?>, class InstantAdapter : JsonSerializer<Instant?>,
JsonDeserializer<Instant?> { JsonDeserializer<Instant?> {
override fun serialize( override fun serialize(

View File

@ -1,13 +1,16 @@
package dev.solsynth.solian.data package dev.solsynth.solian.data
import androidx.annotation.Keep
import java.time.Instant import java.time.Instant
@Keep
data class SolarUser( data class SolarUser(
val id: Int, val id: Int,
val name: String, val name: String,
val nick: String val nick: String
) )
@Keep
data class SolarCheckInRecord( data class SolarCheckInRecord(
val id: Int, val id: Int,
val resultTier: Int, val resultTier: Int,

View File

@ -3,7 +3,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.glance.Button
import androidx.glance.GlanceId import androidx.glance.GlanceId
import androidx.glance.GlanceModifier import androidx.glance.GlanceModifier
import androidx.glance.appwidget.GlanceAppWidget import androidx.glance.appwidget.GlanceAppWidget
@ -14,12 +13,12 @@ import androidx.glance.layout.Alignment
import androidx.glance.layout.Column import androidx.glance.layout.Column
import androidx.glance.layout.Row import androidx.glance.layout.Row
import androidx.glance.layout.Spacer import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxHeight
import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.height import androidx.glance.layout.height
import androidx.glance.layout.padding import androidx.glance.layout.padding
import androidx.glance.state.GlanceStateDefinition import androidx.glance.state.GlanceStateDefinition
import androidx.glance.text.FontFamily import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextStyle import androidx.glance.text.TextStyle
import com.google.gson.FieldNamingPolicy import com.google.gson.FieldNamingPolicy
@ -31,6 +30,7 @@ import java.time.OffsetDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
class CheckInWidget : GlanceAppWidget() { class CheckInWidget : GlanceAppWidget() {
override val stateDefinition: GlanceStateDefinition<*>? override val stateDefinition: GlanceStateDefinition<*>?
get() = HomeWidgetGlanceStateDefinition() get() = HomeWidgetGlanceStateDefinition()
@ -56,6 +56,7 @@ class CheckInWidget : GlanceAppWidget() {
Column( Column(
modifier = GlanceModifier modifier = GlanceModifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight()
.background(Color.White) .background(Color.White)
.padding(16.dp) .padding(16.dp)
) { ) {
@ -86,11 +87,6 @@ class CheckInWidget : GlanceAppWidget() {
text = "You haven't checked in today", text = "You haven't checked in today",
style = TextStyle(fontSize = 15.sp) style = TextStyle(fontSize = 15.sp)
) )
Spacer(modifier = GlanceModifier.height(8.dp))
Button(
text = "Check In",
onClick = {}
)
} }
} }
} }

View File

@ -1,7 +1,6 @@
import android.content.Context import android.content.Context
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.glance.GlanceId import androidx.glance.GlanceId
@ -14,6 +13,7 @@ import androidx.glance.layout.Alignment
import androidx.glance.layout.Column import androidx.glance.layout.Column
import androidx.glance.layout.Row import androidx.glance.layout.Row
import androidx.glance.layout.Spacer import androidx.glance.layout.Spacer
import androidx.glance.layout.fillMaxHeight
import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxSize
import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.fillMaxWidth
import androidx.glance.layout.height import androidx.glance.layout.height
@ -24,12 +24,9 @@ import androidx.glance.text.FontFamily
import androidx.glance.text.FontWeight import androidx.glance.text.FontWeight
import androidx.glance.text.Text import androidx.glance.text.Text
import androidx.glance.text.TextStyle 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.FieldNamingPolicy
import com.google.gson.GsonBuilder import com.google.gson.GsonBuilder
import com.google.gson.TypeAdapterFactory
import dev.solsynth.solian.data.InstantAdapter import dev.solsynth.solian.data.InstantAdapter
import dev.solsynth.solian.data.SolarPost import dev.solsynth.solian.data.SolarPost
import java.time.Instant import java.time.Instant
@ -37,7 +34,6 @@ import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
class FeaturedPostWidget : GlanceAppWidget() { class FeaturedPostWidget : GlanceAppWidget() {
override val stateDefinition: GlanceStateDefinition<*>? override val stateDefinition: GlanceStateDefinition<*>?
get() = HomeWidgetGlanceStateDefinition() get() = HomeWidgetGlanceStateDefinition()
@ -48,24 +44,8 @@ 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 @Composable
private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) { private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) {
setSingletonImageLoaderFactory { context ->
ImageLoader.Builder(context)
.crossfade(true)
.build()
}
val gson = val gson =
GsonBuilder() GsonBuilder()
.setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES)
@ -78,13 +58,12 @@ class FeaturedPostWidget : GlanceAppWidget() {
Column( Column(
modifier = GlanceModifier modifier = GlanceModifier
.fillMaxWidth() .fillMaxWidth()
.fillMaxHeight()
.background(Color.White) .background(Color.White)
.padding(16.dp) .padding(16.dp)
) { ) {
if (postFeaturedRaw != null) { if (postFeaturedRaw != null) {
val postFeaturedList: Array<SolarPost> = val postFeatured = gson.fromJson(postFeaturedRaw, SolarPost::class.java)
gson.fromJson(postFeaturedRaw, Array<SolarPost>::class.java)
val postFeatured = postFeaturedList.firstOrNull();
Row { Row {
Text( Text(

View File

@ -1,6 +1,6 @@
<appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android" <appwidget-provider xmlns:android="http://schemas.android.com/apk/res/android"
android:initialLayout="@layout/glance_default_loading_layout" android:initialLayout="@layout/glance_default_loading_layout"
android:minWidth="80dp" android:minWidth="120dp"
android:minHeight="40dp" android:minHeight="40dp"
android:resizeMode="horizontal|vertical" android:resizeMode="horizontal|vertical"
android:updatePeriodMillis="10000"> android:updatePeriodMillis="10000">

14
android/app/src/proguard-rules.pro vendored Normal file
View File

@ -0,0 +1,14 @@
-keepclassmembers class kotlin.Metadata { *; }
-keep class dev.solsynth.solian.** { *; }
-keep public class dev.solsynth.solian.data.** { public *; }
-keepclassmembers class dev.solsynth.solian.data.** { *; }
-keepattributes *Annotation*
-keepattributes Signature
-keepattributes EnclosingMethod
-keep class com.google.gson.** { *; }
-keepclassmembers class * {
@com.google.gson.annotations.SerializedName <fields>;
}

View File

@ -1,4 +1,3 @@
org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError
android.useAndroidX=true android.useAndroidX=true
android.enableJetifier=true android.enableJetifier=true
kotlin.suppressKotlinVersionCompatibilityCheck=true

View File

@ -69,12 +69,16 @@ class _HomeScreenState extends State<HomeScreen> {
body: LayoutBuilder( body: LayoutBuilder(
builder: (context, constraints) { builder: (context, constraints) {
return Align( return Align(
alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter, alignment: constraints.maxWidth > 640
? Alignment.center
: Alignment.topCenter,
child: Container( child: Container(
constraints: const BoxConstraints(maxWidth: 640), constraints: const BoxConstraints(maxWidth: 640),
child: SingleChildScrollView( child: SingleChildScrollView(
child: Column( child: Column(
mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start, mainAxisAlignment: constraints.maxWidth > 640
? MainAxisAlignment.center
: MainAxisAlignment.start,
children: [ children: [
_HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8), _HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8),
StaggeredGrid.extent( StaggeredGrid.extent(
@ -108,7 +112,9 @@ class _HomeDashSpecialDayWidget extends StatelessWidget {
final ua = context.watch<UserProvider>(); final ua = context.watch<UserProvider>();
final today = DateTime.now(); final today = DateTime.now();
final birthday = ua.user?.profile?.birthday?.toLocal(); final birthday = ua.user?.profile?.birthday?.toLocal();
final isBirthday = birthday != null && birthday.day == today.day && birthday.month == today.month; final isBirthday = birthday != null &&
birthday.day == today.day &&
birthday.month == today.month;
return Column( return Column(
children: [ children: [
if (isBirthday) if (isBirthday)
@ -168,15 +174,20 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
} }
Widget _buildDetailChunk(int index, bool positive) { Widget _buildDetailChunk(int index, bool positive) {
final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; final prefix =
final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint';
final mod =
positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount;
final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod); final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod);
return Column( return Column(
crossAxisAlignment: CrossAxisAlignment.start, crossAxisAlignment: CrossAxisAlignment.start,
children: [ children: [
Text( Text(
prefix.tr(args: ['$prefix$pos'.tr()]), prefix.tr(args: ['$prefix$pos'.tr()]),
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
), ),
Text( Text(
'$prefix${pos}Description', '$prefix${pos}Description',
@ -211,7 +222,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
else else
Text( Text(
'dailyCheckEverythingIsNegative', 'dailyCheckEverythingIsNegative',
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
).tr(), ).tr(),
const Gap(8), const Gap(8),
if (_todayRecord?.resultTier != 4) if (_todayRecord?.resultTier != 4)
@ -227,7 +241,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> {
else else
Text( Text(
'dailyCheckEverythingIsPositive', 'dailyCheckEverythingIsPositive',
style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), style: Theme.of(context)
.textTheme
.titleMedium!
.copyWith(fontWeight: FontWeight.bold),
).tr(), ).tr(),
], ],
), ),
@ -345,10 +362,12 @@ class _HomeDashNotificationWidget extends StatefulWidget {
const _HomeDashNotificationWidget({super.key}); const _HomeDashNotificationWidget({super.key});
@override @override
State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState(); State<_HomeDashNotificationWidget> createState() =>
_HomeDashNotificationWidgetState();
} }
class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> { class _HomeDashNotificationWidgetState
extends State<_HomeDashNotificationWidget> {
int? _count; int? _count;
Future<void> _fetchNotificationCount() async { Future<void> _fetchNotificationCount() async {
@ -385,7 +404,9 @@ class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget
style: Theme.of(context).textTheme.titleLarge, style: Theme.of(context).textTheme.titleLarge,
).tr(), ).tr(),
Text( Text(
_count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0), _count == null
? 'loading'.tr()
: 'notificationUnreadCount'.plural(_count ?? 0),
style: Theme.of(context).textTheme.bodyLarge, style: Theme.of(context).textTheme.bodyLarge,
), ),
], ],
@ -416,10 +437,12 @@ class _HomeDashRecommendationPostWidget extends StatefulWidget {
const _HomeDashRecommendationPostWidget({super.key}); const _HomeDashRecommendationPostWidget({super.key});
@override @override
State<_HomeDashRecommendationPostWidget> createState() => _HomeDashRecommendationPostWidgetState(); State<_HomeDashRecommendationPostWidget> createState() =>
_HomeDashRecommendationPostWidgetState();
} }
class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendationPostWidget> { class _HomeDashRecommendationPostWidgetState
extends State<_HomeDashRecommendationPostWidget> {
bool _isBusy = false; bool _isBusy = false;
List<SnPost>? _posts; List<SnPost>? _posts;
@ -429,7 +452,7 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati
final pt = context.read<SnPostContentProvider>(); final pt = context.read<SnPostContentProvider>();
final home = context.read<HomeWidgetProvider>(); final home = context.read<HomeWidgetProvider>();
_posts = await pt.listRecommendations(); _posts = await pt.listRecommendations();
home.saveWidgetData('post_featured', _posts!.map((e) => e.toJson()).toList()); home.saveWidgetData('post_featured', _posts!.first.toJson());
} catch (err) { } catch (err) {
if (!mounted) return; if (!mounted) return;
context.showErrorDialog(err); context.showErrorDialog(err);
@ -468,7 +491,8 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati
).padding(horizontal: 18, top: 12, bottom: 8), ).padding(horizontal: 18, top: 12, bottom: 8),
Expanded( Expanded(
child: PageView.builder( child: PageView.builder(
scrollBehavior: ScrollConfiguration.of(context).copyWith(dragDevices: { scrollBehavior:
ScrollConfiguration.of(context).copyWith(dragDevices: {
PointerDeviceKind.mouse, PointerDeviceKind.mouse,
PointerDeviceKind.touch, PointerDeviceKind.touch,
}), }),
@ -481,7 +505,8 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati
showMenu: false, showMenu: false,
).padding(bottom: 8), ).padding(bottom: 8),
onTap: () { onTap: () {
GoRouter.of(context).pushNamed('postDetail', pathParameters: { GoRouter.of(context)
.pushNamed('postDetail', pathParameters: {
'slug': _posts![index].id.toString(), 'slug': _posts![index].id.toString(),
}); });
}, },