diff --git a/AppVersionChecker/build.gradle.kts b/AppVersionChecker/build.gradle.kts
new file mode 100644
index 000000000..406bfef20
--- /dev/null
+++ b/AppVersionChecker/build.gradle.kts
@@ -0,0 +1,53 @@
+/*
+ * Infomaniak SwissTransfer - Android
+ * Copyright (C) 2025-2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+plugins {
+ id("com.android.library")
+ alias(core.plugins.kotlin.android)
+ kotlin("plugin.serialization")
+}
+
+val coreCompileSdk: Int by rootProject.extra
+val coreMinSdk: Int by rootProject.extra
+val javaVersion: JavaVersion by rootProject.extra
+
+android {
+ namespace = "com.infomaniak.core.appversionchecker"
+ compileSdk = coreCompileSdk
+
+ defaultConfig {
+ minSdk = coreMinSdk
+ }
+
+ compileOptions {
+ sourceCompatibility = javaVersion
+ targetCompatibility = javaVersion
+ }
+
+ kotlinOptions {
+ jvmTarget = javaVersion.toString()
+ }
+}
+
+dependencies {
+ implementation(project(":Core"))
+ implementation(project(":Core:Network"))
+ implementation(project(":Core:Sentry"))
+
+ implementation(core.kotlinx.serialization.json)
+ implementation(core.okhttp)
+}
diff --git a/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRepositoryAppVersion.kt b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRepositoryAppVersion.kt
new file mode 100644
index 000000000..91c713e3e
--- /dev/null
+++ b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRepositoryAppVersion.kt
@@ -0,0 +1,58 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.appversionchecker.data.api
+
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
+import com.infomaniak.core.network.api.ApiController
+import com.infomaniak.core.network.api.ApiController.ApiMethod
+import com.infomaniak.core.network.models.ApiResponse
+import okhttp3.OkHttpClient
+
+object ApiRepositoryAppVersion {
+ suspend fun getAppVersion(
+ appName: String,
+ store: AppVersion.Store,
+ okHttpClient: OkHttpClient
+ ): ApiResponse {
+ return ApiController.callApi(ApiRoutesAppVersion.appVersion(appName, store), ApiMethod.GET, okHttpClient = okHttpClient)
+ }
+
+ /**
+ * Get app version with projection to have a lighter JSON
+ *
+ * @param appName: App package name
+ * @param store: Specify which store is check
+ * @param projectionFields: A List of fields to keep to build response
+ * @param channelFilter: A optional parameter to filter by distribution channel.
+ * @param okHttpClient
+ */
+ suspend fun getAppVersion(
+ appName: String,
+ store: AppVersion.Store,
+ projectionFields: List,
+ channelFilter: AppVersion.VersionChannel,
+ okHttpClient: OkHttpClient
+ ): ApiResponse {
+ return ApiController.callApi(
+ url = ApiRoutesAppVersion.appVersion(appName, store, projectionFields, channelFilter),
+ method = ApiMethod.GET,
+ okHttpClient = okHttpClient,
+ useKotlinxSerialization = true
+ )
+ }
+}
diff --git a/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRoutesAppVersion.kt b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRoutesAppVersion.kt
new file mode 100644
index 000000000..2eeee78da
--- /dev/null
+++ b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/api/ApiRoutesAppVersion.kt
@@ -0,0 +1,43 @@
+/*
+ * Infomaniak Core - Android
+ * Copyright (C) 2025 Infomaniak Network SA
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see .
+ */
+package com.infomaniak.core.appversionchecker.data.api
+
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
+import com.infomaniak.core.network.INFOMANIAK_API_V1
+
+internal object ApiRoutesAppVersion {
+ fun appVersion(appName: String, store: AppVersion.Store): String {
+ val platform = AppVersion.Platform.ANDROID.apiValue
+
+ return "${INFOMANIAK_API_V1}/app-information/versions/${store.apiValue}/$platform/$appName"
+ }
+
+ fun appVersion(
+ appName: String,
+ store: AppVersion.Store,
+ projectionFields: List,
+ channelFilter: AppVersion.VersionChannel?
+ ): String {
+ val parameters = buildString {
+ projectionFields.takeIf { it.isNotEmpty() }?.let { append("?only=${projectionFields.joinToString(",") { it.value }}") }
+ channelFilter?.let { append("&filter_versions[]=$channelFilter") }
+ }
+
+ return appVersion(appName, store) + parameters
+ }
+}
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppPublishedVersion.kt b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppPublishedVersion.kt
similarity index 56%
rename from InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppPublishedVersion.kt
rename to AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppPublishedVersion.kt
index db6962098..5873c2466 100644
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppPublishedVersion.kt
+++ b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppPublishedVersion.kt
@@ -15,9 +15,24 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package com.infomaniak.core.inappupdate.updaterequired.data.models
+package com.infomaniak.core.appversionchecker.data.models
+import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
-data class AppPublishedVersion(var tag: String)
+data class AppPublishedVersion(
+ val tag: String? = null,
+ @SerialName("tag_updated_at")
+ val tagUpdatedAt: String? = null,
+ @SerialName("version_changelog")
+ val versionChangelog: String? = null,
+ val type: String? = null,
+ @SerialName("build_version")
+ val buildVersion: String? = null,
+ @SerialName("build_min_os_version")
+ val buildMinOsVersion: String? = null,
+ @SerialName("download_link")
+ val downloadLink: String? = null,
+ val data: List? = null
+)
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppVersion.kt b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppVersion.kt
similarity index 68%
rename from InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppVersion.kt
rename to AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppVersion.kt
index a55d18570..c036ac687 100644
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/models/AppVersion.kt
+++ b/AppVersionChecker/src/main/kotlin/com/infomaniak/core/appversionchecker/data/models/AppVersion.kt
@@ -15,19 +15,26 @@
* You should have received a copy of the GNU General Public License
* along with this program. If not, see .
*/
-package com.infomaniak.core.inappupdate.updaterequired.data.models
+package com.infomaniak.core.appversionchecker.data.models
-import io.sentry.Sentry
-import io.sentry.SentryLevel
+import com.infomaniak.core.sentry.SentryLog
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class AppVersion(
+ val id: Int? = null,
+ val name: String? = null,
+ val platform: String? = null,
+ val store: String? = null,
+ @SerialName("api_id")
+ val apiId: String? = null,
@SerialName("min_version")
- var minimalAcceptedVersion: String,
+ val minimalAcceptedVersion: String? = null,
+ @SerialName("next_version_rate")
+ val nextVersionRate: String? = null,
@SerialName("published_versions")
- var publishedVersions: List,
+ val publishedVersions: List? = null,
) {
enum class Store(val apiValue: String) {
@@ -39,7 +46,25 @@ data class AppVersion(
ANDROID("android")
}
+ enum class ProjectionFields(val value: String) {
+ MinVersion("min_version"),
+ PublishedVersionsTag("published_versions.tag"),
+ PublishedVersionType("published_versions.type"),
+ PublishedVersionBuildVersion("published_versions.build_version"),
+ PublishedVersionMinOs("published_versions.build_min_os_version")
+ }
+
+ enum class VersionChannel(val value: String) {
+ Production("production"),
+ Beta("beta"),
+ Internal("internal"),
+ }
+
fun mustRequireUpdate(currentVersion: String): Boolean = runCatching {
+ if (minimalAcceptedVersion == null) {
+ SentryLog.d(TAG, "min_version field is empty. Don't forget to use AppVersion.ProjectionFields.MinVersion")
+ return false
+ }
val currentVersionNumbers = currentVersion.toVersionNumbers()
val minimalAcceptedVersionNumbers = minimalAcceptedVersion.toVersionNumbers()
@@ -47,8 +72,7 @@ data class AppVersion(
return isMinimalVersionValid(minimalAcceptedVersionNumbers) &&
currentVersionNumbers.compareVersionTo(minimalAcceptedVersionNumbers) < 0
}.getOrElse { exception ->
- Sentry.captureException(exception) { scope ->
- scope.level = SentryLevel.ERROR
+ SentryLog.e(TAG, exception.message ?: "Exception occurred during app checking", exception) { scope ->
scope.setExtra("Version from API", minimalAcceptedVersion)
scope.setExtra("Current Version", currentVersion)
}
@@ -57,13 +81,15 @@ data class AppVersion(
}
fun isMinimalVersionValid(minimalVersionNumbers: List): Boolean {
- val productionVersion = publishedVersions.singleOrNull()?.tag ?: return false
+ val productionVersion = publishedVersions?.singleOrNull()?.tag ?: return false
val productionVersionNumbers = productionVersion.toVersionNumbers()
return minimalVersionNumbers.compareVersionTo(productionVersionNumbers) <= 0
}
companion object {
+ const val TAG = "AppVersion"
+
fun String.toVersionNumbers() = split(".").map(String::toInt)
/**
diff --git a/InAppUpdate/build.gradle.kts b/InAppUpdate/build.gradle.kts
index aa409bdfb..93cc5a4b4 100644
--- a/InAppUpdate/build.gradle.kts
+++ b/InAppUpdate/build.gradle.kts
@@ -50,6 +50,7 @@ android {
dependencies {
implementation(project(":Core"))
+ implementation(project(":Core:AppVersionChecker"))
implementation(project(":Core:Compose:Margin"))
implementation(project(":Core:Network"))
implementation(project(":Core:Sentry"))
@@ -65,7 +66,6 @@ dependencies {
implementation(core.androidx.concurrent.futures.ktx)
implementation(core.okhttp)
- implementation(core.gson)
// Compose
implementation(platform(core.compose.bom))
diff --git a/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt b/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt
deleted file mode 100644
index 46b5b25f0..000000000
--- a/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt
+++ /dev/null
@@ -1,25 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.inappupdate
-
-import com.infomaniak.core.inappupdate.updaterequired.data.models.AppVersion.Store
-
-object StoreUtils : StoresUtils {
- const val APP_UPDATE_TAG = "appUpdateFDroid"
- override val REQUIRED_UPDATE_STORE = Store.FDROID
-}
diff --git a/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt b/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
index 7133ea0ed..d2dbd0333 100644
--- a/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
+++ b/InAppUpdate/src/fdroid/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
@@ -19,12 +19,12 @@ package com.infomaniak.core.inappupdate.updatemanagers
import androidx.activity.ComponentActivity
import androidx.lifecycle.lifecycleScope
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
import com.infomaniak.core.network.NetworkConfiguration.appId
import com.infomaniak.core.network.NetworkConfiguration.appVersionCode
import com.infomaniak.core.inappupdate.FdroidApiTools
import com.infomaniak.core.sentry.SentryLog
import com.infomaniak.core.inappupdate.BaseInAppUpdateManager
-import com.infomaniak.core.inappupdate.StoreUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@@ -33,8 +33,11 @@ class InAppUpdateManager(
private val activity: ComponentActivity,
) : BaseInAppUpdateManager(activity) {
+ override val store: AppVersion.Store = AppVersion.Store.FDROID
+ override val appUpdateTag: String = "appUpdateFDroid"
+
override fun checkUpdateIsAvailable() {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "Checking for update on FDroid")
+ SentryLog.d(appUpdateTag, "Checking for update on FDroid")
activity.lifecycleScope.launch(Dispatchers.IO) {
val lastVersionCode = FdroidApiTools().getLastRelease(appId)
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/BaseInAppUpdateManager.kt b/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/BaseInAppUpdateManager.kt
index b4e8ac239..6ed69d599 100644
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/BaseInAppUpdateManager.kt
+++ b/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/BaseInAppUpdateManager.kt
@@ -22,12 +22,13 @@ import androidx.datastore.preferences.core.Preferences
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.lifecycleScope
+import com.infomaniak.core.appversionchecker.data.api.ApiRepositoryAppVersion
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
import com.infomaniak.core.extensions.goToAppStore
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.APP_UPDATE_LAUNCHES_KEY
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.DEFAULT_APP_UPDATE_LAUNCHES
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.HAS_APP_UPDATE_DOWNLOADED_KEY
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.IS_USER_WANTING_UPDATES_KEY
-import com.infomaniak.core.inappupdate.updaterequired.data.api.ApiRepositoryStores
import com.infomaniak.core.network.NetworkConfiguration.appId
import com.infomaniak.core.network.NetworkConfiguration.appVersionName
import com.infomaniak.core.network.networking.HttpClient
@@ -40,6 +41,9 @@ import kotlinx.coroutines.launch
abstract class BaseInAppUpdateManager(private val activity: ComponentActivity) : DefaultLifecycleObserver {
+ abstract val store: AppVersion.Store
+ abstract val appUpdateTag: String
+
protected var onInAppUpdateUiChange: ((Boolean) -> Unit)? = null
protected var onFDroidResult: ((Boolean) -> Unit)? = null
@@ -51,7 +55,17 @@ abstract class BaseInAppUpdateManager(private val activity: ComponentActivity) :
.flowOf(HAS_APP_UPDATE_DOWNLOADED_KEY).distinctUntilChanged()
val isUpdateRequired = flow {
- val apiResponse = ApiRepositoryStores.getAppVersion(appId, HttpClient.okHttpClient)
+ val projectionFields = listOf(
+ AppVersion.ProjectionFields.MinVersion,
+ AppVersion.ProjectionFields.PublishedVersionsTag
+ )
+ val apiResponse = ApiRepositoryAppVersion.getAppVersion(
+ appName = appId,
+ store = store,
+ projectionFields = projectionFields,
+ channelFilter = AppVersion.VersionChannel.Production,
+ okHttpClient = HttpClient.okHttpClient
+ )
emit(apiResponse.data?.mustRequireUpdate(appVersionName) == true)
}.stateIn(
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/StoresUtils.kt b/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/StoresUtils.kt
deleted file mode 100644
index 504d3a3bd..000000000
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/StoresUtils.kt
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.inappupdate
-
-import com.infomaniak.core.inappupdate.updaterequired.data.models.AppVersion
-
-internal interface StoresUtils {
-
- @Suppress("PropertyName")
- val REQUIRED_UPDATE_STORE: AppVersion.Store
-}
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRepositoryStores.kt b/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRepositoryStores.kt
deleted file mode 100644
index 7f43a98fd..000000000
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRepositoryStores.kt
+++ /dev/null
@@ -1,30 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.inappupdate.updaterequired.data.api
-
-import com.infomaniak.core.inappupdate.updaterequired.data.models.AppVersion
-import com.infomaniak.core.network.api.ApiController
-import com.infomaniak.core.network.api.ApiController.ApiMethod
-import com.infomaniak.core.network.models.ApiResponse
-import okhttp3.OkHttpClient
-
-object ApiRepositoryStores {
- suspend fun getAppVersion(appName: String, okHttpClient: OkHttpClient): ApiResponse {
- return ApiController.callApi(ApiRoutesStores.appVersion(appName), ApiMethod.GET, okHttpClient = okHttpClient)
- }
-}
diff --git a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRoutesStores.kt b/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRoutesStores.kt
deleted file mode 100644
index 3e8d21591..000000000
--- a/InAppUpdate/src/main/kotlin/com/infomaniak/core/inappupdate/updaterequired/data/api/ApiRoutesStores.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.inappupdate.updaterequired.data.api
-
-import com.infomaniak.core.inappupdate.StoreUtils
-import com.infomaniak.core.inappupdate.updaterequired.data.models.AppVersion.Platform
-import com.infomaniak.core.network.INFOMANIAK_API_V1
-
-object ApiRoutesStores {
-
- fun appVersion(appName: String): String {
- val store = StoreUtils.REQUIRED_UPDATE_STORE.apiValue
- val platform = Platform.ANDROID.apiValue
-
- val parameters = "?only=min_version,published_versions.tag&filter_versions[]=production"
-
- return "${INFOMANIAK_API_V1}/app-information/versions/$store/$platform/$appName$parameters"
- }
-}
diff --git a/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt b/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt
deleted file mode 100644
index 55882223b..000000000
--- a/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/StoreUtils.kt
+++ /dev/null
@@ -1,27 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.inappupdate
-
-import com.google.android.play.core.install.model.AppUpdateType
-import com.infomaniak.core.inappupdate.updaterequired.data.models.AppVersion.Store
-
-object StoreUtils : StoresUtils {
- const val APP_UPDATE_TAG = "inAppUpdate"
- const val DEFAULT_UPDATE_TYPE = AppUpdateType.FLEXIBLE
- override val REQUIRED_UPDATE_STORE = Store.PLAY_STORE
-}
diff --git a/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt b/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
index fe4317076..cf15de1b2 100644
--- a/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
+++ b/InAppUpdate/src/standard/kotlin/com/infomaniak/core/inappupdate/updatemanagers/InAppUpdateManager.kt
@@ -33,12 +33,12 @@ import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.install.model.InstallStatus
import com.google.android.play.core.install.model.UpdateAvailability
import com.google.android.play.core.ktx.installErrorCode
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.APP_UPDATE_LAUNCHES_KEY
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.DEFAULT_APP_UPDATE_LAUNCHES
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.HAS_APP_UPDATE_DOWNLOADED_KEY
import com.infomaniak.core.inappupdate.AppUpdateSettingsRepository.Companion.IS_USER_WANTING_UPDATES_KEY
import com.infomaniak.core.inappupdate.BaseInAppUpdateManager
-import com.infomaniak.core.inappupdate.StoreUtils
import com.infomaniak.core.sentry.SentryLog
import io.sentry.Sentry
import io.sentry.SentryLevel
@@ -55,6 +55,9 @@ class InAppUpdateManager(
private val activity: ComponentActivity,
) : BaseInAppUpdateManager(activity) {
+ override val store: AppVersion.Store = AppVersion.Store.PLAY_STORE
+ override val appUpdateTag: String = APP_UPDATE_TAG
+
private val appUpdateManager = AppUpdateManagerFactory.create(activity)
// Result of in app update's bottomSheet user choice
private val inAppUpdateResultLauncher: ActivityResultLauncher = activity.registerForActivityResult(
@@ -74,25 +77,25 @@ class InAppUpdateManager(
private var onInstallFailure: ((Exception) -> Unit)? = null
private var onInstallSuccess: (() -> Unit)? = null
- private var updateType: Int = StoreUtils.DEFAULT_UPDATE_TYPE
+ private var updateType: Int = DEFAULT_UPDATE_TYPE
// Create a listener to track request state updates.
private val installStateUpdatedListener by lazy {
InstallStateUpdatedListener { state ->
when (state.installStatus()) {
InstallStatus.DOWNLOADED -> {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "OnUpdateDownloaded triggered by InstallStateUpdated listener")
+ SentryLog.d(appUpdateTag, "OnUpdateDownloaded triggered by InstallStateUpdated listener")
if (updateType == AppUpdateType.FLEXIBLE) onUpdateDownloaded()
}
InstallStatus.FAILED -> {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "onInstallFailure triggered by InstallStateUpdated listener")
+ SentryLog.d(appUpdateTag, "onInstallFailure triggered by InstallStateUpdated listener")
if (updateType == AppUpdateType.IMMEDIATE) {
resetUpdateSettings()
onInstallFailure?.invoke(InstallException(state.installErrorCode))
}
}
InstallStatus.INSTALLED -> {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "OnUpdateInstalled triggered by InstallStateUpdated listener")
+ SentryLog.d(appUpdateTag, "OnUpdateInstalled triggered by InstallStateUpdated listener")
if (updateType == AppUpdateType.FLEXIBLE) onUpdateInstalled()
unregisterAppUpdateListener()
}
@@ -138,13 +141,13 @@ class InAppUpdateManager(
}
override fun checkUpdateIsAvailable() {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "Checking for update on GPlay")
+ SentryLog.d(appUpdateTag, "Checking for update on GPlay")
appUpdateManager.appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "checking success")
+ SentryLog.d(appUpdateTag, "checking success")
if (appUpdateInfo.updateAvailability() == UpdateAvailability.UPDATE_AVAILABLE
&& appUpdateInfo.isUpdateTypeAllowed(updateType)
) {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "Update available on GPlay")
+ SentryLog.d(appUpdateTag, "Update available on GPlay")
startUpdateFlow(appUpdateInfo)
}
}
@@ -191,7 +194,7 @@ class InAppUpdateManager(
registerListener(installStateUpdatedListener)
appUpdateInfo.addOnSuccessListener { appUpdateInfo ->
if (appUpdateInfo.installStatus() == InstallStatus.DOWNLOADED) {
- SentryLog.d(StoreUtils.APP_UPDATE_TAG, "CheckStalledUpdate downloaded")
+ SentryLog.d(appUpdateTag, "CheckStalledUpdate downloaded")
// If the update is downloaded but not installed, notify the user to complete the update.
onUpdateDownloaded.invoke()
}
@@ -224,4 +227,9 @@ class InAppUpdateManager(
}
private class AppUpdateException(override val message: String?) : Exception()
+
+ companion object {
+ const val APP_UPDATE_TAG = "inAppUpdate"
+ const val DEFAULT_UPDATE_TYPE = AppUpdateType.FLEXIBLE
+ }
}
diff --git a/Legacy/Stores/build.gradle b/Legacy/Stores/build.gradle
index 48082a814..5396b4ce8 100644
--- a/Legacy/Stores/build.gradle
+++ b/Legacy/Stores/build.gradle
@@ -31,6 +31,7 @@ android {
}
dependencies {
+ implementation project(":Core:AppVersionChecker")
implementation project(':Core:Legacy')
implementation project(":Core:Network") // To access API URL
diff --git a/Legacy/Stores/src/fdroid/java/com/infomaniak/core/legacy/stores/StoreUtils.kt b/Legacy/Stores/src/fdroid/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
index 51a93cba9..9e8e78492 100644
--- a/Legacy/Stores/src/fdroid/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
+++ b/Legacy/Stores/src/fdroid/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
@@ -17,10 +17,10 @@
*/
package com.infomaniak.core.legacy.stores
-import com.infomaniak.core.legacy.stores.updaterequired.data.models.AppVersion.Store
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
object StoreUtils : StoresUtils {
const val APP_UPDATE_TAG = "appUpdateFDroid"
- override val REQUIRED_UPDATE_STORE = Store.FDROID
+ override val REQUIRED_UPDATE_STORE = AppVersion.Store.FDROID
}
diff --git a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/StoresUtils.kt b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/StoresUtils.kt
index d95144405..52adee80b 100644
--- a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/StoresUtils.kt
+++ b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/StoresUtils.kt
@@ -24,10 +24,10 @@ import androidx.annotation.StyleRes
import androidx.core.net.toUri
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.lifecycleScope
+import com.infomaniak.core.appversionchecker.data.api.ApiRepositoryAppVersion
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
import com.infomaniak.core.legacy.networking.HttpClient
import com.infomaniak.core.legacy.stores.updaterequired.UpdateRequiredActivity
-import com.infomaniak.core.legacy.stores.updaterequired.data.api.ApiRepositoryStores
-import com.infomaniak.core.legacy.stores.updaterequired.data.models.AppVersion
import kotlinx.coroutines.launch
internal interface StoresUtils {
@@ -42,7 +42,18 @@ internal interface StoresUtils {
versionCode: Int,
@StyleRes themeRes: Int,
) = lifecycleScope.launch {
- val apiResponse = ApiRepositoryStores.getAppVersion(appId, HttpClient.okHttpClientNoTokenInterceptor)
+ val projectionFields = listOf(
+ AppVersion.ProjectionFields.MinVersion,
+ AppVersion.ProjectionFields.PublishedVersionsTag,
+ )
+
+ val apiResponse = ApiRepositoryAppVersion.getAppVersion(
+ appName = appId,
+ store = REQUIRED_UPDATE_STORE,
+ projectionFields = projectionFields,
+ channelFilter = AppVersion.VersionChannel.Production,
+ okHttpClient = HttpClient.okHttpClientNoTokenInterceptor
+ )
if (apiResponse.data?.mustRequireUpdate(appVersion) == true) {
UpdateRequiredActivity.startUpdateRequiredActivity(this@checkUpdateIsRequired, appId, versionCode, themeRes)
diff --git a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRepositoryStores.kt b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRepositoryStores.kt
deleted file mode 100644
index 6de4f607d..000000000
--- a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRepositoryStores.kt
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2024-2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.legacy.stores.updaterequired.data.api
-
-import com.infomaniak.core.legacy.api.ApiController
-import com.infomaniak.core.legacy.api.ApiController.ApiMethod
-import com.infomaniak.core.legacy.models.ApiResponse
-import com.infomaniak.core.legacy.stores.updaterequired.data.models.AppVersion
-import okhttp3.OkHttpClient
-
-object ApiRepositoryStores {
-
- suspend fun getAppVersion(appName: String, okHttpClient: OkHttpClient): ApiResponse {
- return ApiController.callApi(ApiRoutesStores.appVersion(appName), ApiMethod.GET, okHttpClient = okHttpClient)
- }
-}
diff --git a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRoutesStores.kt b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRoutesStores.kt
deleted file mode 100644
index c394577e5..000000000
--- a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/api/ApiRoutesStores.kt
+++ /dev/null
@@ -1,34 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2024-2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.legacy.stores.updaterequired.data.api
-
-import com.infomaniak.core.legacy.stores.StoreUtils
-import com.infomaniak.core.legacy.stores.updaterequired.data.models.AppVersion.Platform
-import com.infomaniak.core.network.INFOMANIAK_API_V1
-
-object ApiRoutesStores {
-
- fun appVersion(appName: String): String {
- val store = StoreUtils.REQUIRED_UPDATE_STORE.apiValue
- val platform = Platform.ANDROID.apiValue
-
- val parameters = "?only=min_version,published_versions.tag&filter_versions[]=production"
-
- return "$INFOMANIAK_API_V1/app-information/versions/$store/$platform/$appName$parameters"
- }
-}
diff --git a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppPublishedVersion.kt b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppPublishedVersion.kt
deleted file mode 100644
index 1ce7804ec..000000000
--- a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppPublishedVersion.kt
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2024-2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.legacy.stores.updaterequired.data.models
-
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class AppPublishedVersion(var tag: String)
diff --git a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppVersion.kt b/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppVersion.kt
deleted file mode 100644
index 1fd94cea6..000000000
--- a/Legacy/Stores/src/main/java/com/infomaniak/core/legacy/stores/updaterequired/data/models/AppVersion.kt
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * Infomaniak Core - Android
- * Copyright (C) 2024-2025 Infomaniak Network SA
- *
- * This program is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * This program is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- * GNU General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see .
- */
-package com.infomaniak.core.legacy.stores.updaterequired.data.models
-
-import com.google.gson.annotations.SerializedName
-import io.sentry.Sentry
-import io.sentry.SentryLevel
-import kotlinx.serialization.Serializable
-
-@Serializable
-data class AppVersion(
- @SerializedName("min_version")
- var minimalAcceptedVersion: String,
- @SerializedName("published_versions")
- var publishedVersions: List,
-) {
-
- enum class Store(val apiValue: String) {
- PLAY_STORE("google-play-store"),
- FDROID("f-droid")
- }
-
- enum class Platform(val apiValue: String) {
- ANDROID("android")
- }
-
- fun mustRequireUpdate(currentVersion: String): Boolean = runCatching {
-
- val currentVersionNumbers = currentVersion.toVersionNumbers()
- val minimalAcceptedVersionNumbers = minimalAcceptedVersion.toVersionNumbers()
-
- return isMinimalVersionValid(minimalAcceptedVersionNumbers) &&
- currentVersionNumbers.compareVersionTo(minimalAcceptedVersionNumbers) < 0
- }.getOrElse { exception ->
- Sentry.captureException(exception) { scope ->
- scope.level = SentryLevel.ERROR
- scope.setExtra("Version from API", minimalAcceptedVersion)
- scope.setExtra("Current Version", currentVersion)
- }
-
- return false
- }
-
- fun isMinimalVersionValid(minimalVersionNumbers: List): Boolean {
- val productionVersion = publishedVersions.singleOrNull()?.tag ?: return false
- val productionVersionNumbers = productionVersion.toVersionNumbers()
-
- return minimalVersionNumbers.compareVersionTo(productionVersionNumbers) <= 0
- }
-
- companion object {
- fun String.toVersionNumbers() = split(".").map(String::toInt)
-
- /**
- * Compare Two version in the form of two [List] of [Int].
- * These lists must corresponds to the major, minor and patch version values
- *
- * @return -1 if caller version is older than [other] version, 0 if they are equal, 1 if caller is newer
- */
- fun List.compareVersionTo(other: List): Int {
-
- val selfNormalizedList = normalizeVersionNumbers(other)
- val otherNormalizedList = other.normalizeVersionNumbers(this)
-
- selfNormalizedList.forEachIndexed { index, currentNumber ->
- val otherNumber = otherNormalizedList[index]
- if (currentNumber == otherNumber) return@forEachIndexed
-
- return if (currentNumber < otherNumber) -1 else 1
- }
-
- return 0
- }
-
- private fun List.normalizeVersionNumbers(other: List) = toMutableList().apply {
- while (count() < other.count()) add(0)
- }
- }
-}
diff --git a/Legacy/Stores/src/standard/java/com/infomaniak/core/legacy/stores/StoreUtils.kt b/Legacy/Stores/src/standard/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
index a09865622..81c8583e0 100644
--- a/Legacy/Stores/src/standard/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
+++ b/Legacy/Stores/src/standard/java/com/infomaniak/core/legacy/stores/StoreUtils.kt
@@ -20,13 +20,13 @@ package com.infomaniak.core.legacy.stores
import androidx.fragment.app.FragmentActivity
import com.google.android.play.core.install.model.AppUpdateType
import com.google.android.play.core.review.ReviewManagerFactory
-import com.infomaniak.core.legacy.stores.updaterequired.data.models.AppVersion.Store
+import com.infomaniak.core.appversionchecker.data.models.AppVersion
object StoreUtils : StoresUtils {
const val APP_UPDATE_TAG = "inAppUpdate"
const val DEFAULT_UPDATE_TYPE = AppUpdateType.FLEXIBLE
- override val REQUIRED_UPDATE_STORE = Store.PLAY_STORE
+ override val REQUIRED_UPDATE_STORE = AppVersion.Store.PLAY_STORE
//region In-App Review
override fun FragmentActivity.launchInAppReview() {