diff --git a/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubApiService.kt b/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubApiService.kt index 22a14228..44321cc9 100644 --- a/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubApiService.kt +++ b/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubApiService.kt @@ -24,7 +24,7 @@ interface GithubApiService { ): GithubBranchInfo /** - * Retrieves the release notes for the given tag. + * Retrieves the release for the given tag. * * @param tag The tag name. * @return A [GithubReleaseInfo] object. @@ -34,10 +34,17 @@ interface GithubApiService { @Path("tag") tag: String ): GithubReleaseInfo + /** + * Retrieves the latest stable release for the given tag. + * + * @return A [GithubReleaseInfo] object. + */ + @GET("repos/$GITHUB_USERNAME/$GITHUB_REPOSITORY/releases/latest") + suspend fun getStableReleaseInfo(): GithubReleaseInfo + /** * Retrieves the release notes for the given tag. * - * @param tag The tag name. * @return A [GithubReleaseInfo] object. */ @GET("repos/$GITHUB_USERNAME/$GITHUB_REPOSITORY/tags") diff --git a/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubRawApiService.kt b/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubRawApiService.kt index ee2421b1..50b3675b 100644 --- a/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubRawApiService.kt +++ b/core/network/src/main/kotlin/com/flixclusive/core/network/retrofit/GithubRawApiService.kt @@ -2,7 +2,6 @@ package com.flixclusive.core.network.retrofit import com.flixclusive.core.util.common.GithubConstant.GITHUB_CONFIG_REPOSITORY import com.flixclusive.core.util.common.GithubConstant.GITHUB_USERNAME -import com.flixclusive.model.configuration.AppConfig import com.flixclusive.model.configuration.catalog.HomeCatalogsData import com.flixclusive.model.configuration.catalog.SearchCatalogsData import retrofit2.http.GET @@ -20,6 +19,4 @@ interface GithubRawApiService { @GET("$GITHUB_USERNAME/$GITHUB_CONFIG_REPOSITORY/main/search_items_config.json") suspend fun getSearchCatalogsConfig(): SearchCatalogsData - @GET("$GITHUB_USERNAME/$GITHUB_CONFIG_REPOSITORY/main/app.json") - suspend fun getAppConfig(): AppConfig } \ No newline at end of file diff --git a/data/configuration/build.gradle.kts b/data/configuration/build.gradle.kts index 66ed3c7a..0814266b 100644 --- a/data/configuration/build.gradle.kts +++ b/data/configuration/build.gradle.kts @@ -9,11 +9,14 @@ android { } dependencies { - api(projects.core.datastore) api(libs.stubs.util) + api(projects.core.datastore) api(projects.model.configuration) implementation(libs.mockk) implementation(projects.core.locale) implementation(projects.core.network) + + + testImplementation(libs.retrofit.gson) } \ No newline at end of file diff --git a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppConfigurationManager.kt b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppConfigurationManager.kt index 4d7c20eb..f78556a9 100644 --- a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppConfigurationManager.kt +++ b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppConfigurationManager.kt @@ -1,17 +1,13 @@ package com.flixclusive.data.configuration import com.flixclusive.core.datastore.AppSettingsManager -import com.flixclusive.core.locale.UiText -import com.flixclusive.core.network.retrofit.GithubApiService import com.flixclusive.core.network.retrofit.GithubRawApiService import com.flixclusive.core.network.util.Resource import com.flixclusive.core.network.util.Resource.Failure.Companion.toNetworkException -import com.flixclusive.core.util.common.GithubConstant.GITHUB_REPOSITORY -import com.flixclusive.core.util.common.GithubConstant.GITHUB_USERNAME import com.flixclusive.core.util.coroutines.AppDispatchers +import com.flixclusive.core.util.coroutines.AppDispatchers.Companion.launchOnIO import com.flixclusive.core.util.log.errorLog import com.flixclusive.core.util.network.okhttp.UserAgentManager -import com.flixclusive.model.configuration.AppConfig import com.flixclusive.model.configuration.catalog.HomeCatalogsData import com.flixclusive.model.configuration.catalog.SearchCatalogsData import kotlinx.coroutines.Job @@ -27,16 +23,6 @@ import javax.inject.Inject import javax.inject.Singleton import com.flixclusive.core.locale.R as LocaleR -sealed class UpdateStatus( - val errorMessage: UiText? = null -) { - data object Fetching: UpdateStatus() - data object Maintenance : UpdateStatus() - data object Outdated : UpdateStatus() - data object UpToDate : UpdateStatus() - class Error(errorMessage: UiText?) : UpdateStatus(errorMessage) -} - /** * * Substitute model for BuildConfig @@ -55,7 +41,7 @@ private const val MAX_RETRIES = 5 @Singleton class AppConfigurationManager @Inject constructor( private val githubRawApiService: GithubRawApiService, - private val githubApiService: GithubApiService, + private val appUpdateChecker: AppUpdateChecker, private val appSettingsManager: AppSettingsManager, client: OkHttpClient, ) { @@ -72,16 +58,17 @@ class AppConfigurationManager @Inject constructor( var currentAppBuild: AppBuild? = null private set - var appConfig: AppConfig? = null + var appUpdateInfo: AppUpdateInfo? = null var homeCatalogsData: HomeCatalogsData? = null var searchCatalogsData: SearchCatalogsData? = null private val Resource.needsToInitialize: Boolean get() = (this is Resource.Success - && (appConfig == null || homeCatalogsData == null || searchCatalogsData == null)) + && (appUpdateInfo == null || homeCatalogsData == null || searchCatalogsData == null)) + || this is Resource.Failure init { - AppDispatchers.Default.scope.launch { + launchOnIO { _configurationStatus.collectLatest { if(it.needsToInitialize) initialize(currentAppBuild) @@ -93,10 +80,10 @@ class AppConfigurationManager @Inject constructor( if(fetchJob?.isActive == true) return - if(this.currentAppBuild == null) - this.currentAppBuild = appBuild + if(currentAppBuild == null) + currentAppBuild = appBuild - fetchJob = AppDispatchers.Default.scope.launch { + fetchJob = AppDispatchers.IO.scope.launch { val retryDelay = 3000L for (i in 0..MAX_RETRIES) { _configurationStatus.update { Resource.Loading } @@ -135,50 +122,21 @@ class AppConfigurationManager @Inject constructor( val appSettings = appSettingsManager.appSettings.data.first() val isUsingPrereleaseUpdates = appSettings.isUsingPrereleaseUpdates - appConfig = githubRawApiService.getAppConfig() - - if(appConfig!!.isMaintenance) - return _updateStatus.update { UpdateStatus.Maintenance } - - if (isUsingPrereleaseUpdates && currentAppBuild?.debug == false) { - val lastCommitObject = githubApiService.getLastCommitObject() - val appCommitVersion = currentAppBuild?.commitVersion - ?: throw NullPointerException("appCommitVersion should not be null!") - - val preReleaseTag = "pre-release" - val preReleaseTagInfo = githubApiService.getTagsInfo().find { it.name == preReleaseTag } - - val shortenedSha = lastCommitObject.lastCommit.sha.shortenSha() - val isNeedingAnUpdate = appCommitVersion != shortenedSha - && lastCommitObject.lastCommit.sha == preReleaseTagInfo?.lastCommit?.sha - - if (isNeedingAnUpdate) { - val preReleaseReleaseInfo = githubApiService.getReleaseInfo(tag = preReleaseTag) - - appConfig = appConfig!!.copy( - versionName = "PR-$shortenedSha \uD83D\uDDFF", - updateInfo = preReleaseReleaseInfo.releaseNotes, - updateUrl = "https://github.com/$GITHUB_USERNAME/$GITHUB_REPOSITORY/releases/download/pre-release/flixclusive-release.apk" - ) - - _updateStatus.update { UpdateStatus.Outdated } - return - } - - _updateStatus.update { UpdateStatus.UpToDate } - return + val status = if (isUsingPrereleaseUpdates && currentAppBuild?.debug == false) { + appUpdateChecker.checkForPrereleaseUpdates( + currentAppBuild = currentAppBuild!! + ) } else { - val isNeedingAnUpdate = appConfig!!.build != -1L && appConfig!!.build > currentAppBuild!!.build - - if(isNeedingAnUpdate) { - val releaseInfo = githubApiService.getReleaseInfo(tag = appConfig!!.versionName) - - appConfig = appConfig!!.copy(updateInfo = releaseInfo.releaseNotes) - return _updateStatus.update { UpdateStatus.Outdated } - } + appUpdateChecker.checkForStableUpdates( + currentAppBuild = currentAppBuild!! + ) + } - return _updateStatus.update { UpdateStatus.UpToDate } + if (status is UpdateStatus.Outdated) { + appUpdateInfo = status.updateInfo } + + _updateStatus.update { status } } catch (e: Exception) { errorLog(e) val errorMessageId = e.toNetworkException().error!! @@ -186,7 +144,4 @@ class AppConfigurationManager @Inject constructor( _updateStatus.update { UpdateStatus.Error(errorMessageId) } } } - - private fun String.shortenSha() - = substring(0, 7) } \ No newline at end of file diff --git a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateChecker.kt b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateChecker.kt new file mode 100644 index 00000000..86ff49dd --- /dev/null +++ b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateChecker.kt @@ -0,0 +1,128 @@ +package com.flixclusive.data.configuration + +import com.flixclusive.core.network.retrofit.GithubApiService +import com.flixclusive.core.util.common.GithubConstant +import retrofit2.HttpException +import java.time.Instant +import javax.inject.Inject + +internal const val PRE_RELEASE_TAG = "pre-release" + +class AppUpdateChecker @Inject constructor( + private val githubApiService: GithubApiService, +) { + suspend fun checkForPrereleaseUpdates(currentAppBuild: AppBuild): UpdateStatus { + return safeNetworkCall(currentAppBuild) { currentAppUpdateInfo -> + val lastCommitObject = githubApiService.getLastCommitObject() + val appCommitVersion = currentAppBuild.commitVersion + + val preReleaseTagInfo = + githubApiService.getTagsInfo().find { it.name == PRE_RELEASE_TAG } + + val shortenedSha = lastCommitObject.lastCommit.shortSha + val isNeedingAnUpdate = appCommitVersion != shortenedSha + && lastCommitObject.lastCommit.sha == preReleaseTagInfo?.lastCommit?.sha + + if (isNeedingAnUpdate) { + val preReleaseReleaseInfo = githubApiService.getReleaseInfo(tag = PRE_RELEASE_TAG) + + val newAppConfig = AppUpdateInfo( + versionName = "PR-$shortenedSha \uD83D\uDDFF", + updateInfo = preReleaseReleaseInfo.releaseNotes, + updateUrl = "https://github.com/${GithubConstant.GITHUB_USERNAME}/${GithubConstant.GITHUB_REPOSITORY}/releases/download/$PRE_RELEASE_TAG/flixclusive-release.apk" + ) + + return UpdateStatus.Outdated(updateInfo = newAppConfig) + } + + return UpdateStatus.UpToDate(updateInfo = currentAppUpdateInfo) + } + } + + suspend fun checkForStableUpdates(currentAppBuild: AppBuild): UpdateStatus { + return safeNetworkCall(currentAppBuild) { currentAppUpdateInfo -> + val latestStableRelease = githubApiService.getStableReleaseInfo() + val currentReleaseInfo = + githubApiService.getReleaseInfo(tag = currentAppBuild.versionName) + + val latestReleaseCreationDate = + Instant.parse(latestStableRelease.createdAt).toEpochMilli() + val currentReleaseCreationDate = + Instant.parse(currentReleaseInfo.createdAt).toEpochMilli() + + val latestSemVer = parseSemVer(latestStableRelease.name) + val currentSemVer = parseSemVer(currentAppBuild.versionName) + + val isNeedingAnUpdate = latestReleaseCreationDate > currentReleaseCreationDate + && latestSemVer > currentSemVer + + if (isNeedingAnUpdate) { + val newAppUpdateInfo = AppUpdateInfo( + versionName = latestStableRelease.name, + updateInfo = latestStableRelease.releaseNotes, + updateUrl = "https://github.com/${GithubConstant.GITHUB_USERNAME}/${GithubConstant.GITHUB_REPOSITORY}/releases/download/${latestStableRelease.name}/flixclusive-release.apk" + ) + + return UpdateStatus.Outdated(updateInfo = newAppUpdateInfo) + } + + return UpdateStatus.UpToDate(updateInfo = currentAppUpdateInfo) + } + } + + private inline fun safeNetworkCall( + currentAppBuild: AppBuild, + block: (AppUpdateInfo) -> UpdateStatus + ): UpdateStatus { + val currentAppUpdateInfo = AppUpdateInfo( + versionName = currentAppBuild.versionName, + updateUrl = "https://github.com/${GithubConstant.GITHUB_USERNAME}/${GithubConstant.GITHUB_REPOSITORY}/releases/download/${currentAppBuild.versionName}/flixclusive-release.apk" + ) + + try { + return block.invoke(currentAppUpdateInfo) + } catch (e: HttpException) { + val body = e.response()?.errorBody()?.string() + if (e.code() == 404 || body?.contains("Not Found") == true) { + return UpdateStatus.UpToDate(updateInfo = currentAppUpdateInfo) + } + + throw e + } + } + + private fun parseSemVer(version: String): SemanticVersion { + val regex = Regex("""(\d+)\.(\d+)\.(\d+)""") + val match = regex.find(version) + + return match?.let { + val (major, minor, patch) = it.destructured + SemanticVersion(major.toInt(), minor.toInt(), patch.toInt()) + } ?: SemanticVersion( + major = -1, + minor = -1, + patch = -1 + ) + } + + private data class SemanticVersion( + val major: Int, + val minor: Int, + val patch: Int + ) : Comparable { + override fun compareTo(other: SemanticVersion): Int { + // Compare major versions + if (this.major != other.major) { + return this.major.compareTo(other.major) + } + + // Compare minor versions + if (this.minor != other.minor) { + return this.minor.compareTo(other.minor) + } + + // Compare patch versions + return this.patch.compareTo(other.patch) + } + } +} \ No newline at end of file diff --git a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateInfo.kt b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateInfo.kt new file mode 100644 index 00000000..690a24d0 --- /dev/null +++ b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/AppUpdateInfo.kt @@ -0,0 +1,7 @@ +package com.flixclusive.data.configuration + +data class AppUpdateInfo( + val versionName: String, + val updateUrl: String, + val updateInfo: String? = null, +) \ No newline at end of file diff --git a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/UpdateStatus.kt b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/UpdateStatus.kt new file mode 100644 index 00000000..b19aa0fe --- /dev/null +++ b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/UpdateStatus.kt @@ -0,0 +1,12 @@ +package com.flixclusive.data.configuration + +import com.flixclusive.core.locale.UiText + +sealed class UpdateStatus( + val errorMessage: UiText? = null +) { + data object Fetching : UpdateStatus() + data class Outdated(val updateInfo: AppUpdateInfo) : UpdateStatus() + data class UpToDate(val updateInfo: AppUpdateInfo) : UpdateStatus() + class Error(errorMessage: UiText?) : UpdateStatus(errorMessage) +} \ No newline at end of file diff --git a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/di/test/TestAppConfigurationModule.kt b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/di/test/TestAppConfigurationModule.kt index d3b78068..8c5c29cd 100644 --- a/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/di/test/TestAppConfigurationModule.kt +++ b/data/configuration/src/main/kotlin/com/flixclusive/data/configuration/di/test/TestAppConfigurationModule.kt @@ -2,10 +2,10 @@ package com.flixclusive.data.configuration.di.test import com.flixclusive.core.util.network.json.fromJson import com.flixclusive.data.configuration.AppConfigurationManager +import com.flixclusive.data.configuration.AppUpdateInfo import com.flixclusive.data.configuration.di.test.constant.APP_CONFIG import com.flixclusive.data.configuration.di.test.constant.HOME_CATEGORIES import com.flixclusive.data.configuration.di.test.constant.SEARCH_CATEGORIES -import com.flixclusive.model.configuration.AppConfig import com.flixclusive.model.configuration.catalog.HomeCatalogsData import com.flixclusive.model.configuration.catalog.SearchCatalogsData import io.mockk.every @@ -19,12 +19,12 @@ object TestAppConfigurationModule { fun getMockAppConfigurationManager(): AppConfigurationManager { val homeCatalogsDataMock = fromJson(HOME_CATEGORIES) val searchCatalogsDataMock = fromJson(SEARCH_CATEGORIES) - val appConfigMock = fromJson(APP_CONFIG) + val appUpdateInfoMock = fromJson(APP_CONFIG) val mock = mockk { every { homeCatalogsData } returns homeCatalogsDataMock every { searchCatalogsData } returns searchCatalogsDataMock - every { appConfig } returns appConfigMock + every { appUpdateInfo } returns appUpdateInfoMock } return mock diff --git a/data/configuration/src/test/kotlin/com/flixclusive/data/configuration/AppUpdateCheckerTest.kt b/data/configuration/src/test/kotlin/com/flixclusive/data/configuration/AppUpdateCheckerTest.kt new file mode 100644 index 00000000..ffc6d52f --- /dev/null +++ b/data/configuration/src/test/kotlin/com/flixclusive/data/configuration/AppUpdateCheckerTest.kt @@ -0,0 +1,85 @@ +package com.flixclusive.data.configuration + +import com.flixclusive.core.network.retrofit.GithubApiService +import com.flixclusive.core.util.common.GithubConstant +import kotlinx.coroutines.runBlocking +import okhttp3.OkHttpClient +import org.junit.Assert.assertTrue +import org.junit.Before +import org.junit.Test +import retrofit2.Retrofit +import retrofit2.converter.gson.GsonConverterFactory + +class AppUpdateCheckerTest { + + private lateinit var githubApiService: GithubApiService + + private lateinit var appUpdateChecker: AppUpdateChecker + + private val baseAppBuild = AppBuild( + build = 10000, + versionName = "v1.4.0-beta1", + commitVersion = "abc123", + debug = false, + applicationId = "", + applicationName = "" + ) + + @Before + fun setup() { + githubApiService = Retrofit.Builder() + .baseUrl(GithubConstant.GITHUB_API_BASE_URL) + .addConverterFactory(GsonConverterFactory.create()) + .client(OkHttpClient()) + .build() + .create(GithubApiService::class.java) + + appUpdateChecker = AppUpdateChecker(githubApiService) + } + + @Test + fun `checkForPrereleaseUpdates should return Outdated when update is available`() = runBlocking { + // Act + val result = appUpdateChecker.checkForPrereleaseUpdates( + currentAppBuild = baseAppBuild + ) + + // Assert + assertTrue(result is UpdateStatus.Outdated) + } + + @Test + fun `checkForPrereleaseUpdates should return UpToDate when no update is available`() = runBlocking { + // Arrange + val currentAppBuild = baseAppBuild.copy(commitVersion = "5fe0a77") + + // Act + val result = appUpdateChecker.checkForPrereleaseUpdates( + currentAppBuild = currentAppBuild + ) + + // Assert + assertTrue(result is UpdateStatus.UpToDate) + } + + @Test + fun `checkForStableUpdates should return Outdated when update is available`() = runBlocking { + // Act + val result = appUpdateChecker.checkForStableUpdates(baseAppBuild) + + // Assert + assertTrue(result is UpdateStatus.Outdated) + } + + @Test + fun `checkForStableUpdates should return UpToDate when no update is available`() = runBlocking { + // Arrange + val currentAppBuild = baseAppBuild.copy(versionName = "2.0.1") + + // Act + val result = appUpdateChecker.checkForStableUpdates(currentAppBuild) + + // Assert + assertTrue(result is UpdateStatus.UpToDate) + } +} \ No newline at end of file diff --git a/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/DefaultTMDBRepository.kt b/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/DefaultTMDBRepository.kt index 5137ddeb..2fb8f7d9 100644 --- a/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/DefaultTMDBRepository.kt +++ b/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/DefaultTMDBRepository.kt @@ -21,13 +21,12 @@ import com.flixclusive.model.film.util.filterOutUnreleasedFilms import retrofit2.HttpException import javax.inject.Inject +const val TMDB_API_KEY: String = "8d6d91941230817f7807d643736e8a49" + internal class DefaultTMDBRepository @Inject constructor( private val tmdbApiService: TMDBApiService, private val configurationProvider: AppConfigurationManager ) : TMDBRepository { - override val tmdbApiKey: String - get() = configurationProvider.appConfig!!.tmdbApiKey - override suspend fun getTrending( mediaType: String, timeWindow: String, @@ -38,7 +37,7 @@ internal class DefaultTMDBRepository @Inject constructor( val response = tmdbApiService.getTrending( mediaType = mediaType, timeWindow = timeWindow, - apiKey = tmdbApiKey, + apiKey = TMDB_API_KEY, page = page ) @@ -66,7 +65,7 @@ internal class DefaultTMDBRepository @Inject constructor( val response = tmdbApiService.discoverFilms( mediaType = mediaType, - apiKey = tmdbApiKey, + apiKey = TMDB_API_KEY, page = page, sortBy = sortOption, networks = withNetworks?.joinToString(",") ?: "", @@ -94,7 +93,7 @@ internal class DefaultTMDBRepository @Inject constructor( val response = tmdbApiService.search( mediaType = getMediaTypeFromInt(filter), - apiKey = tmdbApiKey, + apiKey = TMDB_API_KEY, page = page, query = query ) @@ -114,7 +113,7 @@ internal class DefaultTMDBRepository @Inject constructor( try { val response = tmdbApiService.getImages( mediaType = mediaType, - apiKey = tmdbApiKey, + apiKey = TMDB_API_KEY, id = id ) @@ -132,7 +131,7 @@ internal class DefaultTMDBRepository @Inject constructor( return withIOContext { try { val movie = tmdbApiService.getMovie( - id = id, apiKey = tmdbApiKey + id = id, apiKey = TMDB_API_KEY ) val collection: TMDBCollection? = if (movie.collection != null) { @@ -176,7 +175,7 @@ internal class DefaultTMDBRepository @Inject constructor( return withIOContext { try { val tvShow = tmdbApiService.getTvShow( - id = id, apiKey = tmdbApiKey + id = id, apiKey = TMDB_API_KEY ) val filteredSeasons = tvShow.seasons @@ -212,7 +211,7 @@ internal class DefaultTMDBRepository @Inject constructor( return withIOContext { try { val season = tmdbApiService.getSeason( - id = id, seasonNumber = seasonNumber, apiKey = tmdbApiKey + id = id, seasonNumber = seasonNumber, apiKey = TMDB_API_KEY ) Resource.Success(season) @@ -249,7 +248,7 @@ internal class DefaultTMDBRepository @Inject constructor( return withIOContext { try { val response = tmdbApiService.getCollection( - id = id, apiKey = tmdbApiKey + id = id, apiKey = TMDB_API_KEY ) Resource.Success(response) @@ -264,7 +263,7 @@ internal class DefaultTMDBRepository @Inject constructor( page: Int, ): Resource> { return withIOContext { - val fullUrl = "$TMDB_API_BASE_URL$url&page=$page&api_key=$tmdbApiKey" + val fullUrl = "$TMDB_API_BASE_URL$url&page=$page&api_key=$TMDB_API_KEY" try { val response = tmdbApiService.get(fullUrl) diff --git a/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/TMDBRepository.kt b/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/TMDBRepository.kt index ea2c92b5..81c2c733 100644 --- a/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/TMDBRepository.kt +++ b/data/tmdb/src/main/kotlin/com/flixclusive/data/tmdb/TMDBRepository.kt @@ -16,8 +16,6 @@ enum class SortOptions { } interface TMDBRepository { - val tmdbApiKey: String - suspend fun getMovie( id: Int ): Resource diff --git a/domain/updater/src/main/kotlin/com/flixclusive/domain/updater/AppUpdateCheckerUseCase.kt b/domain/updater/src/main/kotlin/com/flixclusive/domain/updater/AppUpdateCheckerUseCase.kt index 4c0d1eb3..8a2edf28 100644 --- a/domain/updater/src/main/kotlin/com/flixclusive/domain/updater/AppUpdateCheckerUseCase.kt +++ b/domain/updater/src/main/kotlin/com/flixclusive/domain/updater/AppUpdateCheckerUseCase.kt @@ -9,11 +9,11 @@ class AppUpdateCheckerUseCase @Inject constructor( private val appConfigurationManager: AppConfigurationManager, ) { val newVersion: String? - get() = appConfigurationManager.appConfig?.versionName + get() = appConfigurationManager.appUpdateInfo?.versionName val updateInfo: String? - get() = appConfigurationManager.appConfig?.updateInfo + get() = appConfigurationManager.appUpdateInfo?.updateInfo val updateUrl: String? - get() = appConfigurationManager.appConfig?.updateUrl + get() = appConfigurationManager.appUpdateInfo?.updateUrl val updateStatus = appConfigurationManager.updateStatus diff --git a/feature/mobile/update/src/main/kotlin/com/flixclusive/feature/mobile/update/UpdateDialog.kt b/feature/mobile/update/src/main/kotlin/com/flixclusive/feature/mobile/update/UpdateDialog.kt index 8ed3b2d4..f0c9bfd6 100644 --- a/feature/mobile/update/src/main/kotlin/com/flixclusive/feature/mobile/update/UpdateDialog.kt +++ b/feature/mobile/update/src/main/kotlin/com/flixclusive/feature/mobile/update/UpdateDialog.kt @@ -32,8 +32,8 @@ import com.flixclusive.core.ui.common.navigation.navigator.UpdateDialogNavigator import com.flixclusive.data.configuration.UpdateStatus import com.ramcosta.composedestinations.annotation.Destination import com.ramcosta.composedestinations.spec.DestinationStyle -import com.flixclusive.core.ui.common.R as UiCommonR import com.flixclusive.core.locale.R as LocaleR +import com.flixclusive.core.ui.common.R as UiCommonR internal object DismissibleDialog : DestinationStyle.Dialog { override val properties = DialogProperties( @@ -56,7 +56,7 @@ internal fun UpdateDialog( LaunchedEffect(updateStatus) { if ( - updateStatus == UpdateStatus.Outdated + updateStatus is UpdateStatus.Outdated && viewModel.appUpdateCheckerUseCase.updateUrl != null && viewModel.appUpdateCheckerUseCase.newVersion != null ) { @@ -140,7 +140,7 @@ internal fun UpdateDialog( } AnimatedVisibility( - visible = updateStatus == UpdateStatus.UpToDate, + visible = updateStatus is UpdateStatus.UpToDate, enter = fadeIn(), exit = fadeOut() ) { diff --git a/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt b/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt index 8f5df411..9a9d61c8 100644 --- a/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt +++ b/feature/splash-screen/src/main/kotlin/com/flixclusive/feature/splashScreen/SplashScreen.kt @@ -2,7 +2,6 @@ package com.flixclusive.feature.splashScreen import android.os.Build import androidx.compose.animation.AnimatedContent -import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.core.animateDpAsState import androidx.compose.animation.graphics.ExperimentalAnimationGraphicsApi import androidx.compose.animation.graphics.res.animatedVectorResource @@ -43,11 +42,11 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.max import androidx.hilt.navigation.compose.hiltViewModel import androidx.lifecycle.compose.collectAsStateWithLifecycle +import com.flixclusive.core.network.util.Resource import com.flixclusive.core.theme.FlixclusiveTheme import com.flixclusive.core.ui.common.GradientCircularProgressIndicator import com.flixclusive.core.ui.common.navigation.navigator.SplashScreenNavigator import com.flixclusive.core.ui.common.util.ifElse -import com.flixclusive.core.network.util.Resource import com.flixclusive.data.configuration.UpdateStatus import com.flixclusive.feature.splashScreen.component.ErrorDialog import com.flixclusive.feature.splashScreen.component.PrivacyNotice @@ -58,13 +57,11 @@ import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.shouldShowRationale import com.ramcosta.composedestinations.annotation.Destination import kotlinx.coroutines.delay -import com.flixclusive.core.ui.common.R as UiCommonR import com.flixclusive.core.locale.R as LocaleR +import com.flixclusive.core.ui.common.R as UiCommonR -@OptIn(ExperimentalAnimationGraphicsApi::class, ExperimentalPermissionsApi::class, - ExperimentalAnimationApi::class -) +@OptIn(ExperimentalAnimationGraphicsApi::class, ExperimentalPermissionsApi::class) @Destination @Composable internal fun SplashScreen( @@ -243,7 +240,7 @@ internal fun SplashScreen( } else areAllPermissionsGranted = true if (areAllPermissionsGranted && isDoneAnimating && !onBoardingPreferences.isFirstTimeUserLaunch_) { - if (updateStatus == UpdateStatus.Outdated && appSettings.isUsingAutoUpdateAppFeature) { + if (updateStatus is UpdateStatus.Outdated && appSettings.isUsingAutoUpdateAppFeature) { navigator.openUpdateScreen( newVersion = viewModel.appUpdateCheckerUseCase.newVersion!!, updateInfo = viewModel.appUpdateCheckerUseCase.updateInfo, @@ -251,33 +248,20 @@ internal fun SplashScreen( isComingFromSplashScreen = true, ) } else if ( - ((updateStatus is UpdateStatus.Error || updateStatus == UpdateStatus.Maintenance) && appSettings.isUsingAutoUpdateAppFeature) + ((updateStatus is UpdateStatus.Error) && appSettings.isUsingAutoUpdateAppFeature) || configurationStatus is Resource.Failure - ) - { - val (title, description) = if (updateStatus == UpdateStatus.Maintenance) { - Pair( - stringResource(LocaleR.string.splash_maintenance_header), - stringResource(LocaleR.string.splash_maintenance_message) - ) - } else { - val errorMessage = if (updateStatus is UpdateStatus.Error) - updateStatus.errorMessage - else (configurationStatus as Resource.Failure).error - - Pair( - stringResource(LocaleR.string.something_went_wrong), - errorMessage!!.asString() - ) - } + ) { + val errorMessage = if (updateStatus is UpdateStatus.Error) + updateStatus.errorMessage + else (configurationStatus as Resource.Failure).error ErrorDialog( - title = title, - description = description, + title = stringResource(LocaleR.string.something_went_wrong), + description = errorMessage!!.asString(), onDismiss = navigator::onExitApplication ) } else if ( - (((updateStatus == UpdateStatus.UpToDate) && appSettings.isUsingAutoUpdateAppFeature) + (((updateStatus is UpdateStatus.UpToDate) && appSettings.isUsingAutoUpdateAppFeature) || configurationStatus is Resource.Success) && uiState is SplashScreenUiState.Okay ) { diff --git a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/AppConfig.kt b/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/AppConfig.kt deleted file mode 100644 index 509cd554..00000000 --- a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/AppConfig.kt +++ /dev/null @@ -1,12 +0,0 @@ -package com.flixclusive.model.configuration - -import com.google.gson.annotations.SerializedName - -data class AppConfig( - val isMaintenance: Boolean, - val build: Long, - @SerializedName("build_codename") val versionName: String, - @SerializedName("update_url") val updateUrl: String, - @SerializedName("update_info") val updateInfo: String? = null, - @SerializedName("tmdb_api_key") val tmdbApiKey: String, -) \ No newline at end of file diff --git a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubCommit.kt b/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubCommit.kt index 1bb1c8a7..57634dd3 100644 --- a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubCommit.kt +++ b/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubCommit.kt @@ -2,4 +2,7 @@ package com.flixclusive.model.configuration data class GithubCommit( val sha: String, -) \ No newline at end of file +) { + val shortSha: String + get() = sha.substring(0, 7) +} \ No newline at end of file diff --git a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubReleaseInfo.kt b/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubReleaseInfo.kt index ba349409..9c769ee9 100644 --- a/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubReleaseInfo.kt +++ b/model/configuration/src/main/kotlin/com/flixclusive/model/configuration/GithubReleaseInfo.kt @@ -3,5 +3,8 @@ package com.flixclusive.model.configuration import com.google.gson.annotations.SerializedName data class GithubReleaseInfo( - @SerializedName("body") val releaseNotes: String + @SerializedName("body") val releaseNotes: String, + @SerializedName("prerelease") val isPrerelease: Boolean, + @SerializedName("created_at") val createdAt: String, + val name: String, ) \ No newline at end of file