Skip to content

Commit b9b5335

Browse files
committed
refactor(details): Improve download cache logic and cleanup ViewModel
This commit refactors the `DetailsViewModel` to enhance the download caching mechanism and remove obsolete code. The asset download logic is now more robust. It verifies not only the existence of a cached file but also confirms that its size matches the expected size from the asset metadata, preventing the use of incomplete or stale downloads. The ViewModel's code has also been streamlined by removing redundant comments and unused logic, particularly around download caching and downgrade warnings, improving overall readability and maintenance. - **feat(details)**: Added a file size check to the download cache logic, ensuring that a cached asset is only reused if its size matches the expected size. - **refactor(details)**: Removed obsolete comments and simplified code within the `DetailsViewModel`, including cleanup of the `onCleared` method. - **fix(deeplink)**: Improved the regex in `DeepLinkParser` to more accurately extract GitHub URLs from surrounding text by better handling trailing characters. - **chore(build)**: Overhauled and organized the ProGuard rules for release builds, ensuring proper shrinking and optimization for libraries like Ktor, Koin, Room, and Compose. - **fix(i18n)**: Corrected a minor typo in the Bengali (`bn`) translation for "hours ago".
1 parent d5f7960 commit b9b5335

5 files changed

Lines changed: 185 additions & 112 deletions

File tree

build-logic/convention/src/main/kotlin/AndroidApplicationConventionPlugin.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,11 @@ class AndroidApplicationConventionPlugin : Plugin<Project> {
3232
getByName("release") {
3333
isMinifyEnabled = true
3434
isShrinkResources = true
35+
36+
proguardFiles(
37+
getDefaultProguardFile("proguard-android-optimize.txt"),
38+
"proguard-rules.pro"
39+
)
3540
}
3641
}
3742

composeApp/proguard-rules.pro

Lines changed: 175 additions & 98 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,201 @@
1-
# === CRITICAL: Keep Everything for Networking ===
2-
-keeppackagenames io.ktor.**
3-
-keeppackagenames okhttp3.**
4-
-keeppackagenames okio.**
5-
6-
# Kotlin
7-
-keep class kotlin.** { *; }
8-
-keep class kotlinx.** { *; }
9-
-keepclassmembers class kotlin.** { *; }
10-
11-
# Coroutines
12-
-keep class kotlinx.coroutines.** { *; }
1+
# ============================================================================
2+
# ProGuard / R8 Rules for GitHub Store (KMP + Compose Multiplatform)
3+
# ============================================================================
4+
# Used with: proguard-android-optimize.txt (enables optimization passes)
5+
# ============================================================================
6+
7+
# ── General Attributes ──────────────────────────────────────────────────────
8+
-keepattributes Signature
9+
-keepattributes *Annotation*
10+
-keepattributes InnerClasses,EnclosingMethod
11+
-keepattributes SourceFile,LineNumberTable
12+
-keepattributes Exceptions
13+
14+
# ── Kotlin Core ─────────────────────────────────────────────────────────────
15+
# Keep Kotlin metadata for reflection used by serialization & Koin
16+
-keep class kotlin.Metadata { *; }
17+
-keep class kotlin.reflect.jvm.internal.** { *; }
18+
-dontwarn kotlin.**
19+
-dontwarn kotlinx.**
20+
21+
# ── Kotlin Coroutines ──────────────────────────────────────────────────────
1322
-keepnames class kotlinx.coroutines.internal.MainDispatcherFactory {}
1423
-keepnames class kotlinx.coroutines.CoroutineExceptionHandler {}
1524
-keepclassmembernames class kotlinx.** { volatile <fields>; }
25+
-dontwarn kotlinx.coroutines.**
1626

17-
# === Ktor - Keep EVERYTHING ===
18-
-keep class io.ktor.** { *; }
19-
-keep interface io.ktor.** { *; }
20-
-keepclassmembers class io.ktor.** { *; }
27+
# ── Kotlinx Serialization ──────────────────────────────────────────────────
28+
# Serialization engine internals
29+
-keep class kotlinx.serialization.** { *; }
30+
-keepclassmembers class kotlinx.serialization.json.** { *** Companion; }
31+
-dontnote kotlinx.serialization.**
32+
33+
# Generated serializers for ALL @Serializable classes
34+
-keep class **$$serializer { *; }
35+
-keepclassmembers @kotlinx.serialization.Serializable class ** {
36+
*** Companion;
37+
*** INSTANCE;
38+
kotlinx.serialization.KSerializer serializer(...);
39+
}
40+
41+
# App @Serializable classes (DTOs, models, navigation routes) across all packages
42+
-keep @kotlinx.serialization.Serializable class zed.rainxch.** { *; }
43+
-keep,includedescriptorclasses class zed.rainxch.**$$serializer { *; }
44+
-keepclassmembers @kotlinx.serialization.Serializable class zed.rainxch.** {
45+
*** Companion;
46+
}
47+
48+
# ── Navigation Routes ──────────────────────────────────────────────────────
49+
# Type-safe navigation requires these classes to survive R8
50+
-keep class zed.rainxch.githubstore.app.navigation.GithubStoreGraph { *; }
51+
-keep class zed.rainxch.githubstore.app.navigation.GithubStoreGraph$* { *; }
52+
53+
# ── Network DTOs – Core Module ─────────────────────────────────────────────
54+
-keep class zed.rainxch.core.data.dto.** { *; }
55+
56+
# ── Network DTOs – Feature Modules ─────────────────────────────────────────
57+
-keep class zed.rainxch.search.data.dto.** { *; }
58+
-keep class zed.rainxch.devprofile.data.dto.** { *; }
59+
-keep class zed.rainxch.home.data.dto.** { *; }
60+
61+
# ── Domain Models ──────────────────────────────────────────────────────────
62+
-keep class zed.rainxch.core.domain.model.GithubRepoSummary { *; }
63+
-keep class zed.rainxch.core.domain.model.GithubUser { *; }
64+
65+
# Keep enums used by Room TypeConverters and serialization
66+
-keep class zed.rainxch.core.domain.model.InstallSource { *; }
67+
-keep class zed.rainxch.core.domain.model.AppTheme { *; }
68+
-keep class zed.rainxch.core.domain.model.FontTheme { *; }
69+
-keep class zed.rainxch.core.domain.model.Platform { *; }
70+
-keep class zed.rainxch.core.domain.model.SystemArchitecture { *; }
71+
-keep class zed.rainxch.core.domain.model.PackageChangeType { *; }
72+
73+
# ── Room Database ──────────────────────────────────────────────────────────
74+
# Database class and generated implementation
75+
-keep class zed.rainxch.core.data.local.db.AppDatabase { *; }
76+
-keep class zed.rainxch.core.data.local.db.AppDatabase_Impl { *; }
77+
78+
# Entities
79+
-keep class zed.rainxch.core.data.local.db.entities.** { *; }
80+
81+
# DAOs
82+
-keep interface zed.rainxch.core.data.local.db.dao.** { *; }
83+
-keep class zed.rainxch.core.data.local.db.dao.** { *; }
84+
85+
# Room runtime
86+
-keep class androidx.room.** { *; }
87+
-dontwarn androidx.room.**
88+
89+
# ── Ktor ───────────────────────────────────────────────────────────────────
90+
# Engine discovery, plugin system, and content negotiation use reflection
91+
-keep class io.ktor.client.engine.** { *; }
92+
-keep class io.ktor.client.plugins.** { *; }
93+
-keep class io.ktor.serialization.** { *; }
94+
-keep class io.ktor.utils.io.** { *; }
95+
-keep class io.ktor.http.** { *; }
2196
-keepnames class io.ktor.** { *; }
2297
-dontwarn io.ktor.**
23-
24-
# Ktor Debug
2598
-dontwarn java.lang.management.**
2699

27-
# === OkHttp - Keep EVERYTHING ===
28-
-keep class okhttp3.** { *; }
29-
-keep interface okhttp3.** { *; }
30-
-keepclassmembers class okhttp3.** { *; }
31-
-keepnames class okhttp3.** { *; }
100+
# ── OkHttp (Ktor engine) ──────────────────────────────────────────────────
101+
-keep class okhttp3.internal.platform.** { *; }
102+
-keepnames class okhttp3.internal.publicsuffix.PublicSuffixDatabase
32103
-dontwarn okhttp3.**
104+
-dontwarn org.bouncycastle.**
105+
-dontwarn org.openjsse.**
33106

34-
# === Okio - Keep EVERYTHING ===
35-
-keep class okio.** { *; }
36-
-keepclassmembers class okio.** { *; }
37-
-keepnames class okio.** { *; }
107+
# ── Okio ───────────────────────────────────────────────────────────────────
38108
-dontwarn okio.**
39109

40-
# === Network Stack - Keep EVERYTHING ===
41-
-keep class java.net.** { *; }
42-
-keep class javax.net.** { *; }
43-
-keep class sun.security.ssl.** { *; }
44-
-keepclassmembers class java.net.** { *; }
45-
-keepclassmembers class javax.net.** { *; }
46-
47-
# DNS Resolution
48-
-keep class java.net.InetAddress { *; }
49-
-keep class java.net.Inet4Address { *; }
50-
-keep class java.net.Inet6Address { *; }
51-
-keep class java.net.InetSocketAddress { *; }
52-
53-
# SSL/TLS
54-
-keep class javax.net.ssl.** { *; }
110+
# ── SSL/TLS ────────────────────────────────────────────────────────────────
55111
-keep class org.conscrypt.** { *; }
56112
-dontwarn org.conscrypt.**
57113

58-
# === Kotlinx Serialization ===
59-
-keepattributes *Annotation*, InnerClasses
60-
-dontnote kotlinx.serialization.**
61-
-keep,includedescriptorclasses class zed.rainxch.githubstore.**$$serializer { *; }
62-
-keep @kotlinx.serialization.Serializable class zed.rainxch.githubstore.** { *; }
63-
-keepclassmembers @kotlinx.serialization.Serializable class zed.rainxch.githubstore.** {
64-
*** Companion;
65-
}
66-
67-
# Keep your models
68-
-keep class zed.rainxch.githubstore.core.domain.model.** { *; }
114+
# ── Koin DI ────────────────────────────────────────────────────────────────
115+
# Koin uses reflection for constructor injection
116+
-keep class org.koin.** { *; }
117+
-keep interface org.koin.** { *; }
118+
-dontwarn org.koin.**
119+
120+
# Keep ViewModels so Koin can instantiate them
121+
-keep class zed.rainxch.**.presentation.**ViewModel { *; }
122+
-keep class zed.rainxch.**.presentation.**ViewModel$* { *; }
123+
124+
# ── Compose / AndroidX ────────────────────────────────────────────────────
125+
# Compose runtime and navigation (most rules come bundled with the library)
126+
-keep class androidx.compose.** { *; }
127+
-dontwarn androidx.compose.**
128+
-keep class androidx.navigation.** { *; }
129+
-keep class androidx.lifecycle.** { *; }
130+
-dontwarn androidx.lifecycle.**
131+
132+
# ── DataStore ──────────────────────────────────────────────────────────────
133+
-keep class androidx.datastore.** { *; }
134+
-keepclassmembers class androidx.datastore.preferences.** { *; }
135+
-dontwarn androidx.datastore.**
136+
137+
# ── Landscapist / Coil3 (Image Loading) ────────────────────────────────────
138+
-keep class com.skydoves.landscapist.** { *; }
139+
-keep interface com.skydoves.landscapist.** { *; }
140+
-keep class coil3.** { *; }
141+
-dontwarn coil3.**
142+
-dontwarn com.skydoves.landscapist.**
143+
144+
# ── Multiplatform Markdown Renderer ────────────────────────────────────────
145+
-keep class com.mikepenz.markdown.** { *; }
146+
-keep class org.intellij.markdown.** { *; }
147+
-dontwarn com.mikepenz.markdown.**
148+
-dontwarn org.intellij.markdown.**
149+
150+
# ── Kermit Logging ─────────────────────────────────────────────────────────
151+
-keep class co.touchlab.kermit.** { *; }
152+
-dontwarn co.touchlab.kermit.**
153+
154+
# ── MOKO Permissions ──────────────────────────────────────────────────────
155+
-keep class dev.icerock.moko.permissions.** { *; }
156+
-dontwarn dev.icerock.moko.**
157+
158+
# ── BuildKonfig (Generated Build Constants) ────────────────────────────────
159+
-keep class zed.rainxch.githubstore.BuildConfig { *; }
160+
-keep class zed.rainxch.**.BuildKonfig { *; }
161+
-keep class **.BuildKonfig { *; }
69162

70-
# === AndroidX Security ===
163+
# ── AndroidX Security / Crypto ─────────────────────────────────────────────
71164
-keep class androidx.security.crypto.** { *; }
72165
-keep class com.google.crypto.tink.** { *; }
73166
-dontwarn com.google.crypto.tink.**
74167
-dontwarn com.google.errorprone.annotations.**
75168

76-
# BuildConfig
77-
-keep class zed.rainxch.githubstore.BuildConfig { *; }
78-
79-
# General
80-
-keepattributes Signature
81-
-keepattributes Exceptions
82-
-keepattributes *Annotation*
83-
-keepattributes SourceFile,LineNumberTable
84-
85-
# === START: Auth Fix ===
86-
-dontoptimize
87-
-keepattributes *Annotation*,Signature,Exception,InnerClasses,EnclosingMethod
169+
# ── Firebase (if integrated) ──────────────────────────────────────────────
170+
-keep class com.google.firebase.** { *; }
171+
-dontwarn com.google.firebase.**
88172

89-
# Keep serialization infrastructure
90-
-keep class kotlinx.serialization.** { *; }
91-
-keep class **$$serializer { *; }
92-
-keepclassmembers @kotlinx.serialization.Serializable class ** {
93-
*** Companion;
94-
*** INSTANCE;
95-
kotlinx.serialization.KSerializer serializer(...);
173+
# ── Enum safety ────────────────────────────────────────────────────────────
174+
# Keep all enum values and valueOf methods (used by serialization/Room)
175+
-keepclassmembers enum * {
176+
public static **[] values();
177+
public static ** valueOf(java.lang.String);
96178
}
97179

98-
# Keep Ktor plugins
99-
-keep class io.ktor.client.plugins.** { *; }
100-
-keep class io.ktor.serialization.** { *; }
101-
102-
# Keep your entire core package (narrow this down later)
103-
-keep class zed.rainxch.githubstore.core.** { *; }
104-
-keepclassmembers class zed.rainxch.githubstore.core.** { *; }
105-
# === END: Auth Fix ===
106-
107-
-keep class zed.rainxch.githubstore.core.data.remote.dto.** { *; }
108-
-keep class zed.rainxch.githubstore.core.domain.model.auth.** { *; }
109-
110-
# If your models are in different packages, list them:
111-
-keep class zed.rainxch.githubstore.**.*DeviceStart* { *; }
112-
-keep class zed.rainxch.githubstore.**.*DeviceToken* { *; }
113-
-keep class zed.rainxch.githubstore.**.*AuthConfig* { *; }
114-
115-
# Keep the companion objects explicitly
116-
-keepclassmembers class zed.rainxch.githubstore.**.DeviceStart {
117-
public static ** Companion;
180+
# ── Parcelable ─────────────────────────────────────────────────────────────
181+
-keepclassmembers class * implements android.os.Parcelable {
182+
public static final ** CREATOR;
118183
}
119-
-keepclassmembers class zed.rainxch.githubstore.**.DeviceTokenSuccess {
120-
public static ** Companion;
184+
185+
# ── ServiceLoader (used by Ktor, Koin, etc.) ──────────────────────────────
186+
-keepnames class * implements java.io.Serializable
187+
-keepclassmembers class * implements java.io.Serializable {
188+
static final long serialVersionUID;
189+
private static final java.io.ObjectStreamField[] serialPersistentFields;
190+
!static !transient <fields>;
191+
private void writeObject(java.io.ObjectOutputStream);
192+
private void readObject(java.io.ObjectInputStream);
193+
java.lang.Object writeReplace();
194+
java.lang.Object readResolve();
121195
}
122-
-keepclassmembers class zed.rainxch.githubstore.**.DeviceTokenError {
123-
public static ** Companion;
124-
}
196+
197+
# ── Suppress Warnings for Missing Classes ──────────────────────────────────
198+
-dontwarn java.lang.invoke.StringConcatFactory
199+
-dontwarn javax.annotation.**
200+
-dontwarn org.slf4j.**
201+
-dontwarn org.codehaus.mojo.animal_sniffer.**

composeApp/src/commonMain/kotlin/zed/rainxch/githubstore/app/deeplink/DeepLinkParser.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -176,7 +176,7 @@ object DeepLinkParser {
176176
}
177177

178178
fun extractSupportedUrl(text: String): String? {
179-
val regex = """https?://(?:www\.)?(?:github\.com|github-store\.org)[^\s<>"']+""".toRegex(RegexOption.IGNORE_CASE)
179+
val regex = """https?://(?:www\.)?(?:github\.com|github-store\.org)(?=[/\s?#]|$)[^\s<>"')\],;.!]*""".toRegex(RegexOption.IGNORE_CASE)
180180
return regex.find(text)?.value
181181
}
182182
}

core/presentation/src/commonMain/composeResources/values-bn/strings-bn.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -367,7 +367,7 @@
367367
<string name="last_checked_never">কখনো পরীক্ষা করা হয়নি</string>
368368
<string name="last_checked_just_now">এইমাত্র</string>
369369
<string name="last_checked_minutes_ago">%1$d মিনিট আগে</string>
370-
<string name="last_checked_hours_ago">%1$d ঘন্টা আগে</string>
370+
<string name="last_checked_hours_ago">%1$d ঘণ্টা আগে</string>
371371
<string name="checking_for_updates">আপডেট পরীক্ষা করা হচ্ছে…</string>
372372

373373
</resources>

0 commit comments

Comments
 (0)