From 7508a54907ce7329f2af7b6094cfe848a73c1605 Mon Sep 17 00:00:00 2001 From: LittleSheep Date: Sun, 15 Dec 2024 21:27:49 +0800 Subject: [PATCH] :bug: Fix android widget don't work fine in release mode --- android/app/build.gradle | 8 +++ .../dev/solsynth/solian/data/SolarPost.kt | 5 +- .../dev/solsynth/solian/data/SolarTime.kt | 3 +- .../dev/solsynth/solian/data/SolarUser.kt | 3 + .../solsynth/solian/widgets/CheckInWidget.kt | 10 +--- .../solian/widgets/FeaturedPostWidget.kt | 29 ++-------- .../app/src/main/res/xml/check_in_widget.xml | 2 +- android/app/src/proguard-rules.pro | 14 +++++ android/gradle.properties | 1 - lib/screens/home.dart | 57 +++++++++++++------ 10 files changed, 80 insertions(+), 52 deletions(-) create mode 100644 android/app/src/proguard-rules.pro diff --git a/android/app/build.gradle b/android/app/build.gradle index daf8223..ca473a3 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -49,10 +49,18 @@ android { } buildTypes { + debug { + minifyEnabled true + shrinkResources true + + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' + } release { // TODO: Add your own signing config for the release build. // Signing with the debug keys for now, so `flutter run --release` works. signingConfig = signingConfigs.debug + + proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' } } } diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarPost.kt b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarPost.kt index 4ce2f5f..c62ed45 100644 --- a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarPost.kt +++ b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarPost.kt @@ -1,7 +1,9 @@ package dev.solsynth.solian.data +import androidx.annotation.Keep import java.time.Instant +@Keep data class SolarPost( val id: Int, val body: SolarPostBody, @@ -13,13 +15,14 @@ data class SolarPost( val publishedAt: Instant? ) +@Keep data class SolarPostBody( val content: String?, val title: String?, val description: String?, - val attachments: List? ) +@Keep data class SolarPublisher( val id: Int, val name: String, diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarTime.kt b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarTime.kt index 121eb68..6c3418a 100644 --- a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarTime.kt +++ b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarTime.kt @@ -1,5 +1,6 @@ package dev.solsynth.solian.data +import androidx.annotation.Keep import com.google.gson.JsonDeserializationContext import com.google.gson.JsonDeserializer import com.google.gson.JsonElement @@ -11,7 +12,7 @@ import java.lang.reflect.Type import java.time.Instant import java.time.format.DateTimeFormatter - +@Keep class InstantAdapter : JsonSerializer, JsonDeserializer { override fun serialize( diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarUser.kt b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarUser.kt index 64f21fa..8433aff 100644 --- a/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarUser.kt +++ b/android/app/src/main/kotlin/dev/solsynth/solian/data/SolarUser.kt @@ -1,13 +1,16 @@ package dev.solsynth.solian.data +import androidx.annotation.Keep import java.time.Instant +@Keep data class SolarUser( val id: Int, val name: String, val nick: String ) +@Keep data class SolarCheckInRecord( val id: Int, val resultTier: Int, diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/widgets/CheckInWidget.kt b/android/app/src/main/kotlin/dev/solsynth/solian/widgets/CheckInWidget.kt index 75e2925..80254d6 100644 --- a/android/app/src/main/kotlin/dev/solsynth/solian/widgets/CheckInWidget.kt +++ b/android/app/src/main/kotlin/dev/solsynth/solian/widgets/CheckInWidget.kt @@ -3,7 +3,6 @@ import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp -import androidx.glance.Button import androidx.glance.GlanceId import androidx.glance.GlanceModifier import androidx.glance.appwidget.GlanceAppWidget @@ -14,12 +13,12 @@ import androidx.glance.layout.Alignment import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxHeight import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.height import androidx.glance.layout.padding 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 com.google.gson.FieldNamingPolicy @@ -31,6 +30,7 @@ import java.time.OffsetDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter + class CheckInWidget : GlanceAppWidget() { override val stateDefinition: GlanceStateDefinition<*>? get() = HomeWidgetGlanceStateDefinition() @@ -56,6 +56,7 @@ class CheckInWidget : GlanceAppWidget() { Column( modifier = GlanceModifier .fillMaxWidth() + .fillMaxHeight() .background(Color.White) .padding(16.dp) ) { @@ -86,11 +87,6 @@ class CheckInWidget : GlanceAppWidget() { text = "You haven't checked in today", style = TextStyle(fontSize = 15.sp) ) - Spacer(modifier = GlanceModifier.height(8.dp)) - Button( - text = "Check In", - onClick = {} - ) } } } diff --git a/android/app/src/main/kotlin/dev/solsynth/solian/widgets/FeaturedPostWidget.kt b/android/app/src/main/kotlin/dev/solsynth/solian/widgets/FeaturedPostWidget.kt index 1a1d988..d3b325b 100644 --- a/android/app/src/main/kotlin/dev/solsynth/solian/widgets/FeaturedPostWidget.kt +++ b/android/app/src/main/kotlin/dev/solsynth/solian/widgets/FeaturedPostWidget.kt @@ -1,7 +1,6 @@ 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 @@ -14,6 +13,7 @@ import androidx.glance.layout.Alignment import androidx.glance.layout.Column import androidx.glance.layout.Row import androidx.glance.layout.Spacer +import androidx.glance.layout.fillMaxHeight import androidx.glance.layout.fillMaxSize import androidx.glance.layout.fillMaxWidth import androidx.glance.layout.height @@ -24,12 +24,9 @@ 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 com.google.gson.TypeAdapterFactory import dev.solsynth.solian.data.InstantAdapter import dev.solsynth.solian.data.SolarPost import java.time.Instant @@ -37,7 +34,6 @@ import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter - class FeaturedPostWidget : GlanceAppWidget() { override val stateDefinition: GlanceStateDefinition<*>? 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 private fun GlanceContent(context: Context, currentState: HomeWidgetGlanceState) { - setSingletonImageLoaderFactory { context -> - ImageLoader.Builder(context) - .crossfade(true) - .build() - } - val gson = GsonBuilder() .setFieldNamingPolicy(FieldNamingPolicy.LOWER_CASE_WITH_UNDERSCORES) @@ -78,13 +58,12 @@ class FeaturedPostWidget : GlanceAppWidget() { Column( modifier = GlanceModifier .fillMaxWidth() + .fillMaxHeight() .background(Color.White) .padding(16.dp) ) { if (postFeaturedRaw != null) { - val postFeaturedList: Array = - gson.fromJson(postFeaturedRaw, Array::class.java) - val postFeatured = postFeaturedList.firstOrNull(); + val postFeatured = gson.fromJson(postFeaturedRaw, SolarPost::class.java) Row { Text( diff --git a/android/app/src/main/res/xml/check_in_widget.xml b/android/app/src/main/res/xml/check_in_widget.xml index 8ea5582..17cf674 100644 --- a/android/app/src/main/res/xml/check_in_widget.xml +++ b/android/app/src/main/res/xml/check_in_widget.xml @@ -1,6 +1,6 @@ diff --git a/android/app/src/proguard-rules.pro b/android/app/src/proguard-rules.pro new file mode 100644 index 0000000..8e1c115 --- /dev/null +++ b/android/app/src/proguard-rules.pro @@ -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 ; +} \ No newline at end of file diff --git a/android/gradle.properties b/android/gradle.properties index 768b3b2..2597170 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,4 +1,3 @@ org.gradle.jvmargs=-Xmx4G -XX:MaxMetaspaceSize=2G -XX:+HeapDumpOnOutOfMemoryError android.useAndroidX=true android.enableJetifier=true -kotlin.suppressKotlinVersionCompatibilityCheck=true diff --git a/lib/screens/home.dart b/lib/screens/home.dart index b07b621..37f0d54 100644 --- a/lib/screens/home.dart +++ b/lib/screens/home.dart @@ -69,12 +69,16 @@ class _HomeScreenState extends State { body: LayoutBuilder( builder: (context, constraints) { return Align( - alignment: constraints.maxWidth > 640 ? Alignment.center : Alignment.topCenter, + alignment: constraints.maxWidth > 640 + ? Alignment.center + : Alignment.topCenter, child: Container( constraints: const BoxConstraints(maxWidth: 640), child: SingleChildScrollView( child: Column( - mainAxisAlignment: constraints.maxWidth > 640 ? MainAxisAlignment.center : MainAxisAlignment.start, + mainAxisAlignment: constraints.maxWidth > 640 + ? MainAxisAlignment.center + : MainAxisAlignment.start, children: [ _HomeDashSpecialDayWidget().padding(top: 8, horizontal: 8), StaggeredGrid.extent( @@ -108,7 +112,9 @@ class _HomeDashSpecialDayWidget extends StatelessWidget { final ua = context.watch(); final today = DateTime.now(); 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( children: [ if (isBirthday) @@ -168,15 +174,20 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { } Widget _buildDetailChunk(int index, bool positive) { - final prefix = positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; - final mod = positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; + final prefix = + positive ? 'dailyCheckPositiveHint' : 'dailyCheckNegativeHint'; + final mod = + positive ? kSuggestionPositiveHintCount : kSuggestionNegativeHintCount; final pos = math.max(1, _todayRecord!.resultModifiers[index] % mod); return Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( 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( '$prefix${pos}Description', @@ -211,7 +222,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { else Text( 'dailyCheckEverythingIsNegative', - style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), ).tr(), const Gap(8), if (_todayRecord?.resultTier != 4) @@ -227,7 +241,10 @@ class _HomeDashCheckInWidgetState extends State<_HomeDashCheckInWidget> { else Text( 'dailyCheckEverythingIsPositive', - style: Theme.of(context).textTheme.titleMedium!.copyWith(fontWeight: FontWeight.bold), + style: Theme.of(context) + .textTheme + .titleMedium! + .copyWith(fontWeight: FontWeight.bold), ).tr(), ], ), @@ -345,10 +362,12 @@ class _HomeDashNotificationWidget extends StatefulWidget { const _HomeDashNotificationWidget({super.key}); @override - State<_HomeDashNotificationWidget> createState() => _HomeDashNotificationWidgetState(); + State<_HomeDashNotificationWidget> createState() => + _HomeDashNotificationWidgetState(); } -class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget> { +class _HomeDashNotificationWidgetState + extends State<_HomeDashNotificationWidget> { int? _count; Future _fetchNotificationCount() async { @@ -385,7 +404,9 @@ class _HomeDashNotificationWidgetState extends State<_HomeDashNotificationWidget style: Theme.of(context).textTheme.titleLarge, ).tr(), Text( - _count == null ? 'loading'.tr() : 'notificationUnreadCount'.plural(_count ?? 0), + _count == null + ? 'loading'.tr() + : 'notificationUnreadCount'.plural(_count ?? 0), style: Theme.of(context).textTheme.bodyLarge, ), ], @@ -416,10 +437,12 @@ class _HomeDashRecommendationPostWidget extends StatefulWidget { const _HomeDashRecommendationPostWidget({super.key}); @override - State<_HomeDashRecommendationPostWidget> createState() => _HomeDashRecommendationPostWidgetState(); + State<_HomeDashRecommendationPostWidget> createState() => + _HomeDashRecommendationPostWidgetState(); } -class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendationPostWidget> { +class _HomeDashRecommendationPostWidgetState + extends State<_HomeDashRecommendationPostWidget> { bool _isBusy = false; List? _posts; @@ -429,7 +452,7 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati final pt = context.read(); final home = context.read(); _posts = await pt.listRecommendations(); - home.saveWidgetData('post_featured', _posts!.map((e) => e.toJson()).toList()); + home.saveWidgetData('post_featured', _posts!.first.toJson()); } catch (err) { if (!mounted) return; context.showErrorDialog(err); @@ -468,7 +491,8 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati ).padding(horizontal: 18, top: 12, bottom: 8), Expanded( child: PageView.builder( - scrollBehavior: ScrollConfiguration.of(context).copyWith(dragDevices: { + scrollBehavior: + ScrollConfiguration.of(context).copyWith(dragDevices: { PointerDeviceKind.mouse, PointerDeviceKind.touch, }), @@ -481,7 +505,8 @@ class _HomeDashRecommendationPostWidgetState extends State<_HomeDashRecommendati showMenu: false, ).padding(bottom: 8), onTap: () { - GoRouter.of(context).pushNamed('postDetail', pathParameters: { + GoRouter.of(context) + .pushNamed('postDetail', pathParameters: { 'slug': _posts![index].id.toString(), }); },