From 3776b9576a186963d46d38b95492c9978d40e6a4 Mon Sep 17 00:00:00 2001 From: Chamika Date: Sun, 17 May 2026 00:44:07 +0100 Subject: [PATCH 1/2] Remove Firebase Analytics and Crashlytics for GMS-free app store distribution Strips all Firebase/Google Play Services dependencies so the APK can be published to alternative stores (e.g. Aurora Store, OEM stores) that do not ship GMS. FirebaseUtils is retained as a Logcat-only stub so all 40 call sites compile unchanged without churn; google-services.json deleted. Co-Authored-By: Claude Opus 4.7 --- automotive/build.gradle.kts | 7 - automotive/google-services.json | 29 ---- .../chamika/dashtune/DashTuneApplication.kt | 25 +-- .../com/chamika/dashtune/FirebaseUtils.kt | 39 +---- .../com/chamika/dashtune/FirebaseUtilsTest.kt | 155 ------------------ .../dashtune/signin/SignInViewModelTest.kt | 4 - build.gradle.kts | 2 - gradle/libs.versions.toml | 8 - 8 files changed, 6 insertions(+), 263 deletions(-) delete mode 100644 automotive/google-services.json delete mode 100644 automotive/src/test/java/com/chamika/dashtune/FirebaseUtilsTest.kt diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts index 5431db5..3814deb 100644 --- a/automotive/build.gradle.kts +++ b/automotive/build.gradle.kts @@ -2,8 +2,6 @@ plugins { alias(libs.plugins.android.application) alias(libs.plugins.ksp) alias(libs.plugins.hilt) - alias(libs.plugins.google.services) - alias(libs.plugins.firebase.crashlytics) } android { @@ -84,11 +82,6 @@ dependencies { implementation(libs.androidx.room.ktx) ksp(libs.androidx.room.compiler) - // Firebase - implementation(platform(libs.firebase.bom)) - implementation(libs.firebase.analytics) - implementation(libs.firebase.crashlytics) - testImplementation(libs.junit) testImplementation(libs.mockk) testImplementation(libs.robolectric) diff --git a/automotive/google-services.json b/automotive/google-services.json deleted file mode 100644 index e07c666..0000000 --- a/automotive/google-services.json +++ /dev/null @@ -1,29 +0,0 @@ -{ - "project_info": { - "project_number": "832350878937", - "project_id": "dashtune-4fc67", - "storage_bucket": "dashtune-4fc67.firebasestorage.app" - }, - "client": [ - { - "client_info": { - "mobilesdk_app_id": "1:832350878937:android:133b2d78b3509e2dfc12c8", - "android_client_info": { - "package_name": "com.chamika.dashtune" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyDDhKYpfNrqaHFXJp2VxSngZSRDCplI4RM" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [] - } - } - } - ], - "configuration_version": "1" -} \ No newline at end of file diff --git a/automotive/src/main/java/com/chamika/dashtune/DashTuneApplication.kt b/automotive/src/main/java/com/chamika/dashtune/DashTuneApplication.kt index 4108289..1e7fb48 100644 --- a/automotive/src/main/java/com/chamika/dashtune/DashTuneApplication.kt +++ b/automotive/src/main/java/com/chamika/dashtune/DashTuneApplication.kt @@ -1,30 +1,7 @@ package com.chamika.dashtune import android.app.Application -import android.util.Log -import com.chamika.dashtune.Constants.LOG_TAG -import com.google.firebase.Firebase -import com.google.firebase.analytics.analytics -import com.google.firebase.crashlytics.crashlytics import dagger.hilt.android.HiltAndroidApp @HiltAndroidApp -class DashTuneApplication : Application() { - override fun onCreate() { - super.onCreate() - - // Disable Firebase collection in debug builds. - // Wrapped in try-catch because Firebase depends on Google Play Services, which may not - // yet be ready shortly after a car software update (GMS can take several minutes to - // initialise on first boot after an OTA). Crashing here would prevent the media service - // from starting and cause the AAOS "Something went wrong" error dialog. - try { - if (BuildConfig.DEBUG) { - Firebase.analytics.setAnalyticsCollectionEnabled(false) - Firebase.crashlytics.setCrashlyticsCollectionEnabled(false) - } - } catch (e: Exception) { - Log.w(LOG_TAG, "Firebase init skipped – Google Play Services not ready yet", e) - } - } -} +class DashTuneApplication : Application() diff --git a/automotive/src/main/java/com/chamika/dashtune/FirebaseUtils.kt b/automotive/src/main/java/com/chamika/dashtune/FirebaseUtils.kt index 8aaa005..a6c18e7 100644 --- a/automotive/src/main/java/com/chamika/dashtune/FirebaseUtils.kt +++ b/automotive/src/main/java/com/chamika/dashtune/FirebaseUtils.kt @@ -2,55 +2,26 @@ package com.chamika.dashtune import android.util.Log import com.chamika.dashtune.Constants.LOG_TAG -import com.google.firebase.crashlytics.FirebaseCrashlytics object FirebaseUtils { - /** - * Records [e] to Firebase Crashlytics, swallowing any exception that might occur if Google - * Play Services is not yet available (e.g. shortly after a car OTA update). - */ fun safeRecordException(e: Exception) { - try { - FirebaseCrashlytics.getInstance().recordException(e) - } catch (crashlyticsError: Exception) { - Log.w(LOG_TAG, "Crashlytics unavailable – Google Play Services not ready yet", crashlyticsError) - } + Log.w(LOG_TAG, "Exception recorded: ${e.message}", e) } - /** Logs [message] to Firebase Crashlytics, safe against GMS unavailability. */ fun safeLog(message: String) { - try { - FirebaseCrashlytics.getInstance().log(message) - } catch (e: Exception) { - Log.w(LOG_TAG, "Crashlytics unavailable – Google Play Services not ready yet", e) - } + Log.i(LOG_TAG, message) } - /** Sets a string custom key on Firebase Crashlytics, safe against GMS unavailability. */ fun safeSetCustomKey(key: String, value: String) { - try { - FirebaseCrashlytics.getInstance().setCustomKey(key, value) - } catch (e: Exception) { - Log.w(LOG_TAG, "Crashlytics unavailable – Google Play Services not ready yet", e) - } + Log.d(LOG_TAG, "[$key] $value") } - /** Sets an int custom key on Firebase Crashlytics, safe against GMS unavailability. */ fun safeSetCustomKey(key: String, value: Int) { - try { - FirebaseCrashlytics.getInstance().setCustomKey(key, value) - } catch (e: Exception) { - Log.w(LOG_TAG, "Crashlytics unavailable – Google Play Services not ready yet", e) - } + Log.d(LOG_TAG, "[$key] $value") } - /** Sets a boolean custom key on Firebase Crashlytics, safe against GMS unavailability. */ fun safeSetCustomKey(key: String, value: Boolean) { - try { - FirebaseCrashlytics.getInstance().setCustomKey(key, value) - } catch (e: Exception) { - Log.w(LOG_TAG, "Crashlytics unavailable – Google Play Services not ready yet", e) - } + Log.d(LOG_TAG, "[$key] $value") } } diff --git a/automotive/src/test/java/com/chamika/dashtune/FirebaseUtilsTest.kt b/automotive/src/test/java/com/chamika/dashtune/FirebaseUtilsTest.kt deleted file mode 100644 index 3e06b60..0000000 --- a/automotive/src/test/java/com/chamika/dashtune/FirebaseUtilsTest.kt +++ /dev/null @@ -1,155 +0,0 @@ -package com.chamika.dashtune - -import com.google.firebase.crashlytics.FirebaseCrashlytics -import io.mockk.every -import io.mockk.just -import io.mockk.mockk -import io.mockk.mockkStatic -import io.mockk.runs -import io.mockk.verify -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.robolectric.RobolectricTestRunner - -@RunWith(RobolectricTestRunner::class) -class FirebaseUtilsTest { - - private lateinit var crashlytics: FirebaseCrashlytics - - @Before - fun setUp() { - crashlytics = mockk(relaxed = true) - mockkStatic(FirebaseCrashlytics::class) - every { FirebaseCrashlytics.getInstance() } returns crashlytics - } - - // --- safeRecordException tests --- - - @Test - fun `safeRecordException records exception to Crashlytics when available`() { - val exception = RuntimeException("test error") - every { crashlytics.recordException(exception) } just runs - - FirebaseUtils.safeRecordException(exception) - - verify { crashlytics.recordException(exception) } - } - - @Test - fun `safeRecordException swallows exception when Crashlytics is unavailable`() { - every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available") - - FirebaseUtils.safeRecordException(RuntimeException("app error")) - // No exception should propagate - } - - @Test - fun `safeRecordException swallows exception when recordException itself throws`() { - val exception = RuntimeException("test error") - every { crashlytics.recordException(any()) } throws IllegalStateException("Crashlytics not initialised") - - FirebaseUtils.safeRecordException(exception) - // No exception should propagate - } - - // --- safeLog tests --- - - @Test - fun `safeLog logs message to Crashlytics when available`() { - every { crashlytics.log(any()) } just runs - - FirebaseUtils.safeLog("login successful") - - verify { crashlytics.log("login successful") } - } - - @Test - fun `safeLog swallows exception when Crashlytics is unavailable`() { - every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available") - - FirebaseUtils.safeLog("some message") - // No exception should propagate - } - - @Test - fun `safeLog swallows exception when log itself throws`() { - every { crashlytics.log(any()) } throws IllegalStateException("Crashlytics not initialised") - - FirebaseUtils.safeLog("some message") - // No exception should propagate - } - - // --- safeSetCustomKey(String, String) tests --- - - @Test - fun `safeSetCustomKey sets string key when Crashlytics is available`() { - every { crashlytics.setCustomKey(any(), any()) } just runs - - FirebaseUtils.safeSetCustomKey("auth_method", "password") - - verify { crashlytics.setCustomKey("auth_method", "password") } - } - - @Test - fun `safeSetCustomKey string overload swallows exception when Crashlytics unavailable`() { - every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available") - - FirebaseUtils.safeSetCustomKey("key", "value") - // No exception should propagate - } - - @Test - fun `safeSetCustomKey string overload swallows exception when setCustomKey throws`() { - every { crashlytics.setCustomKey(any(), any()) } throws RuntimeException("crash") - - FirebaseUtils.safeSetCustomKey("key", "value") - // No exception should propagate - } - - // --- safeSetCustomKey(String, Int) tests --- - - @Test - fun `safeSetCustomKey sets int key when Crashlytics is available`() { - every { crashlytics.setCustomKey(any(), any()) } just runs - - FirebaseUtils.safeSetCustomKey("retry_count", 3) - - verify { crashlytics.setCustomKey("retry_count", 3) } - } - - @Test - fun `safeSetCustomKey int overload swallows exception when Crashlytics unavailable`() { - every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available") - - FirebaseUtils.safeSetCustomKey("key", 42) - // No exception should propagate - } - - // --- safeSetCustomKey(String, Boolean) tests --- - - @Test - fun `safeSetCustomKey sets boolean key when Crashlytics is available`() { - every { crashlytics.setCustomKey(any(), any()) } just runs - - FirebaseUtils.safeSetCustomKey("is_premium", true) - - verify { crashlytics.setCustomKey("is_premium", true) } - } - - @Test - fun `safeSetCustomKey boolean overload swallows exception when Crashlytics unavailable`() { - every { FirebaseCrashlytics.getInstance() } throws RuntimeException("GMS not available") - - FirebaseUtils.safeSetCustomKey("key", false) - // No exception should propagate - } - - @Test - fun `safeSetCustomKey boolean overload swallows exception when setCustomKey throws`() { - every { crashlytics.setCustomKey(any(), any()) } throws RuntimeException("crash") - - FirebaseUtils.safeSetCustomKey("key", true) - // No exception should propagate - } -} diff --git a/automotive/src/test/java/com/chamika/dashtune/signin/SignInViewModelTest.kt b/automotive/src/test/java/com/chamika/dashtune/signin/SignInViewModelTest.kt index bc26fec..6aa2afc 100644 --- a/automotive/src/test/java/com/chamika/dashtune/signin/SignInViewModelTest.kt +++ b/automotive/src/test/java/com/chamika/dashtune/signin/SignInViewModelTest.kt @@ -2,7 +2,6 @@ package com.chamika.dashtune.signin import androidx.arch.core.executor.testing.InstantTaskExecutorRule import com.chamika.dashtune.auth.JellyfinAccountManager -import com.google.firebase.crashlytics.FirebaseCrashlytics import io.mockk.every import io.mockk.mockk import io.mockk.mockkStatic @@ -53,9 +52,6 @@ class SignInViewModelTest { apiClient = mockk(relaxed = true) systemApi = mockk(relaxed = true) - mockkStatic(FirebaseCrashlytics::class) - every { FirebaseCrashlytics.getInstance() } returns mockk(relaxed = true) - // Stub options before createApi so createApi$default can resolve default parameters jellyfin = mockk(relaxed = true) val mockOptions = mockk(relaxed = true) diff --git a/build.gradle.kts b/build.gradle.kts index aea6af5..e6a6fe2 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -3,6 +3,4 @@ plugins { alias(libs.plugins.android.application) apply false alias(libs.plugins.ksp) apply false alias(libs.plugins.hilt) apply false - alias(libs.plugins.google.services) apply false - alias(libs.plugins.firebase.crashlytics) apply false } diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml index 6e713fa..07d06da 100644 --- a/gradle/libs.versions.toml +++ b/gradle/libs.versions.toml @@ -20,9 +20,6 @@ jellyfinSdk = "1.8.6" preferenceKtx = "1.2.1" lifecycleViewmodel = "2.10.0" concurrentFutures = "1.3.0" -firebaseBom = "34.9.0" -googleServices = "4.4.4" -firebaseCrashlyticsPlugin = "3.0.6" slf4jAndroid = "1.7.36" room = "2.7.1" archCoreTesting = "2.2.0" @@ -49,9 +46,6 @@ jellyfin-core = { group = "org.jellyfin.sdk", name = "jellyfin-core", version.re okhttp = { group = "com.squareup.okhttp3", name = "okhttp", version.ref = "okhttp" } hilt-android = { group = "com.google.dagger", name = "hilt-android", version.ref = "hilt" } hilt-compiler = { group = "com.google.dagger", name = "hilt-compiler", version.ref = "hilt" } -firebase-bom = { group = "com.google.firebase", name = "firebase-bom", version.ref = "firebaseBom" } -firebase-analytics = { group = "com.google.firebase", name = "firebase-analytics" } -firebase-crashlytics = { group = "com.google.firebase", name = "firebase-crashlytics" } slf4j-android = { group = "org.slf4j", name = "slf4j-android", version.ref = "slf4jAndroid" } androidx-room-runtime = { group = "androidx.room", name = "room-runtime", version.ref = "room" } androidx-room-ktx = { group = "androidx.room", name = "room-ktx", version.ref = "room" } @@ -64,5 +58,3 @@ android-application = { id = "com.android.application", version.ref = "agp" } kotlin-android = { id = "org.jetbrains.kotlin.android", version.ref = "kotlin" } ksp = { id = "com.google.devtools.ksp", version.ref = "ksp" } hilt = { id = "com.google.dagger.hilt.android", version.ref = "hilt" } -google-services = { id = "com.google.gms.google-services", version.ref = "googleServices" } -firebase-crashlytics = { id = "com.google.firebase.crashlytics", version.ref = "firebaseCrashlyticsPlugin" } From acb2199bb0ccd03f55eb9874d2297ac79b82c00e Mon Sep 17 00:00:00 2001 From: Chamika Date: Sun, 17 May 2026 00:48:30 +0100 Subject: [PATCH 2/2] Release v1.2.3(19) Remove Firebase/Google Play Services for alternative app store distribution Full release notes: - Remove Firebase Analytics and Crashlytics dependencies - App no longer requires Google Play Services to run - Available for distribution on GMS-free app stores (e.g. Aurora Store, OEM stores) --- automotive/build.gradle.kts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/automotive/build.gradle.kts b/automotive/build.gradle.kts index 3814deb..4cf853c 100644 --- a/automotive/build.gradle.kts +++ b/automotive/build.gradle.kts @@ -12,8 +12,8 @@ android { applicationId = "com.chamika.dashtune" minSdk = 28 targetSdk = 36 - versionCode = 18 - versionName = "1.2.2" + versionCode = 19 + versionName = "1.2.3" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" }