diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt index 27ee47abb292371ae7caa80bd5a316d60e1fce4b..3d25bf9ee91dd92be7dc14f8dd85827640bf94ae 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationFragment.kt @@ -18,6 +18,7 @@ package foundation.e.apps.ui.application +import android.annotation.SuppressLint import android.content.Intent import android.graphics.Color import android.graphics.drawable.Drawable @@ -231,12 +232,32 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { fetchAppTracker(it) observeDownloadList() - observeDownloadStatus(binding.root) + updateInstallButton(it) stopLoadingUI() collectState() } + @SuppressLint("SetTextI18n") + private fun updateInstallButton(application: Application) { + binding.downloadInclude.apply { + installButton.disableInstallButton(R.string.install) + installButton.text = "" + progressBarInstall.visibility = View.VISIBLE + } + + if (!isFreeOrAlreadyPurchased(application)) { + appInfoFetchViewModel.isAppPurchased(application).observe(viewLifecycleOwner) { + binding.downloadInclude.progressBarInstall.visibility = View.GONE + observeDownloadStatus(binding.root) + } + return + } + + binding.downloadInclude.progressBarInstall.visibility = View.GONE + observeDownloadStatus(binding.root) + } + private fun collectState() { collectShareVisibilityState() collectAppContentRatingState() @@ -584,13 +605,15 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { /* Remove trailing slash (if present) that can become part of the packageName */ val packageName = args.packageName.run { if (endsWith('/')) dropLast(1) else this } - applicationViewModel.loadData( + val applicationLoadingParams = ApplicationLoadingParams( args.id, packageName, origin, isFdroidDeepLink, - authObjectList - ) { + authObjectList, + args.isPurchased + ) + applicationViewModel.loadData(applicationLoadingParams) { clearAndRestartGPlayLogin() true } @@ -794,33 +817,20 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { mainActivityViewModel.checkUnsupportedApplication(application) -> getString(R.string.not_available) - application.isFree -> getString(R.string.install) + isFreeOrAlreadyPurchased(application) -> getString(R.string.install) else -> application.price } + setOnClickListener { if (mainActivityViewModel.checkUnsupportedApplication(application, activity)) { return@setOnClickListener } applicationIcon?.let { - if (application.isFree) { + if (isFreeOrAlreadyPurchased(application)) { disableInstallButton(R.string.cancel) installApplication(application) } else { - if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(application)) { - ApplicationDialogFragment( - title = getString(R.string.dialog_title_paid_app, application.name), - message = getString( - R.string.dialog_paidapp_message, - application.name, - application.price - ), - positiveButtonText = getString(R.string.dialog_confirm), - positiveButtonAction = { - installApplication(application) - }, - cancelButtonText = getString(R.string.dialog_cancel), - ).show(childFragmentManager, "ApplicationFragment") - } + handleInstallClickForPaidApp(application) } } } @@ -829,6 +839,27 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { appSize.visibility = View.VISIBLE } + private fun isFreeOrAlreadyPurchased(application: Application) = + application.isFree || application.isPurchased + + private fun handleInstallClickForPaidApp(application: Application) { + if (!mainActivityViewModel.shouldShowPaidAppsSnackBar(application)) { + ApplicationDialogFragment( + title = getString(R.string.dialog_title_paid_app, application.name), + message = getString( + R.string.dialog_paidapp_message, + application.name, + application.price + ), + positiveButtonText = getString(R.string.dialog_confirm), + positiveButtonAction = { + installApplication(application) + }, + cancelButtonText = getString(R.string.dialog_cancel), + ).show(childFragmentManager, "ApplicationFragment") + } + } + private fun MaterialButton.disableInstallButton(buttonStringID: Int) { isEnabled = false text = context.getString(buttonStringID) diff --git a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt index e9a0980ae14da9fef1c74f032d6a8dce6d9a513f..3631984268053a4273c9cdadc5130a43ee5ba19d 100644 --- a/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/application/ApplicationViewModel.kt @@ -72,45 +72,66 @@ class ApplicationViewModel @Inject constructor( val appContentRatingState = _appContentRatingState.asStateFlow() fun loadData( - id: String, - packageName: String, - origin: Origin, - isFdroidLink: Boolean, - authObjectList: List, + params: ApplicationLoadingParams, retryBlock: (failedObjects: List) -> Boolean, ) { - if (isFdroidLink) { - getCleanapkAppDetails(packageName) + if (params.isFdroidDeepLink) { + getCleanapkAppDetails(params.packageName) return } - val gPlayObj = authObjectList.find { it is AuthObject.GPlayAuth } + val gPlayObj = params.authObjectList.find { it is AuthObject.GPlayAuth } /* * If user is viewing only open source apps, auth object list will not have * GPlayAuth, it will only have CleanApkAuth. */ - if (gPlayObj == null && origin == Origin.GPLAY) { + if (gPlayObj == null && params.origin == Origin.GPLAY) { _errorMessageLiveData.postValue(R.string.gplay_data_for_oss) return } - super.onLoadData(authObjectList, { successAuthList, _ -> + super.onLoadData(params.authObjectList, { successAuthList, _ -> successAuthList.find { it is AuthObject.GPlayAuth }?.run { - getApplicationDetails(id, packageName, result.data!! as AuthData, origin) + val authData = result.data as? AuthData + // Usually authdata won't be null, null check is added to avoid forcefully unwrapping + if (authData == null) { + _errorMessageLiveData.postValue(R.string.data_load_error) + return@onLoadData + } + + getApplicationDetails( + params.appId, + params.packageName, + params.isPurchased, + authData, + params.origin + ) return@onLoadData } successAuthList.find { it is AuthObject.CleanApk }?.run { - getApplicationDetails(id, packageName, AuthData("", ""), origin) + getApplicationDetails( + params.appId, + params.packageName, + params.isPurchased, + AuthData("", ""), + params.origin + ) return@onLoadData } }, retryBlock) } - fun getApplicationDetails(id: String, packageName: String, authData: AuthData, origin: Origin) { + fun getApplicationDetails( + id: String, + packageName: String, + isPurchased: Boolean, + authData: AuthData, + origin: Origin + ) { viewModelScope.launch(Dispatchers.IO) { try { val appData = @@ -120,6 +141,7 @@ class ApplicationViewModel @Inject constructor( authData, origin ) + appData.first.isPurchased = isPurchased applicationLiveData.postValue(appData) updateShareVisibilityState(appData.first.shareUri.toString()) @@ -247,3 +269,12 @@ sealed class ShareButtonVisibilityState { object Visible : ShareButtonVisibilityState() object Hidden : ShareButtonVisibilityState() } + +data class ApplicationLoadingParams( + val appId: String, + val packageName: String, + val origin: Origin, + val isFdroidDeepLink: Boolean, + val authObjectList: List, + val isPurchased: Boolean +) diff --git a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt index db6b9e8aaad3922348e28a3d414d2736a8edfc92..4987b29ec9012dd707c4d21c71927fee5f351533 100644 --- a/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/applicationlist/ApplicationListRVAdapter.kt @@ -258,7 +258,8 @@ class ApplicationListRVAdapter( searchApp._id, searchApp.origin, catText, - searchApp.isGplayReplaced + searchApp.isGplayReplaced, + searchApp.isPurchased ) } R.id.searchFragment -> { @@ -267,7 +268,8 @@ class ApplicationListRVAdapter( searchApp._id, searchApp.origin, catText, - searchApp.isGplayReplaced + searchApp.isGplayReplaced, + searchApp.isPurchased ) } R.id.updatesFragment -> { @@ -276,7 +278,8 @@ class ApplicationListRVAdapter( searchApp._id, searchApp.origin, catText, - searchApp.isGplayReplaced + searchApp.isGplayReplaced, + searchApp.isPurchased ) } else -> null diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt index faa4373125fdf76ee3d6641c3329111c5339ad88..62886ab2d6194c2c9c0efa93ef8ba124a8373dcf 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/HomeChildRVAdapter.kt @@ -97,7 +97,8 @@ class HomeChildRVAdapter( homeApp._id, homeApp.origin, homeApp.category, - homeApp.isGplayReplaced + homeApp.isGplayReplaced, + homeApp.isPurchased ) holder.itemView.findNavController().navigate(action) } diff --git a/app/src/main/res/layout/fragment_application_download.xml b/app/src/main/res/layout/fragment_application_download.xml index 072f0cc5c5d3fb54afe5aab411857f6703473a7d..e34b6ecabacd3bfb5d1cdf9728c44e5faf34c4e2 100644 --- a/app/src/main/res/layout/fragment_application_download.xml +++ b/app/src/main/res/layout/fragment_application_download.xml @@ -31,6 +31,7 @@ android:id="@+id/progressLayout" android:layout_width="0dp" android:layout_height="wrap_content" + android:visibility="invisible" app:layout_constraintBottom_toBottomOf="@id/installButton" app:layout_constraintTop_toTopOf="parent" app:layout_constraintLeft_toLeftOf="parent" @@ -93,4 +94,15 @@ app:cornerRadius="4dp" /> + + \ No newline at end of file diff --git a/app/src/main/res/navigation/navigation_resource.xml b/app/src/main/res/navigation/navigation_resource.xml index 9077ebf27f7a2b17f4746f51a0b2100e97802ad3..90fae739dc1d077e5a0e8c4b0b1b53384e30aced 100644 --- a/app/src/main/res/navigation/navigation_resource.xml +++ b/app/src/main/res/navigation/navigation_resource.xml @@ -114,6 +114,10 @@ android:name="isGplayReplaced" app:argType="boolean" android:defaultValue="false"/> +