Loading app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +11 −80 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2025 e Foundation * * 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 Loading @@ -14,29 +13,25 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.ui.home import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.enums.Status import foundation.e.apps.data.application.ApplicationInstaller import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.data.enums.Status import foundation.e.apps.databinding.FragmentHomeBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.ui.AppInfoFetchViewModel Loading @@ -45,14 +40,12 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.home.model.HomeChildRVAdapter import foundation.e.apps.ui.home.model.HomeParentRVAdapter import foundation.e.apps.ui.parentFragment.TimeoutFragment import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.Locale import javax.inject.Inject @AndroidEntryPoint class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstaller { class HomeFragment : Fragment(R.layout.fragment_home), ApplicationInstaller { /* * Make adapter nullable to avoid memory leaks. Loading @@ -63,7 +56,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall private val binding get() = _binding!! private val homeViewModel: HomeViewModel by viewModels() override val mainActivityViewModel: MainActivityViewModel by activityViewModels() val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() Loading @@ -74,7 +67,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) loadHomePageData() loadData() homeParentRVAdapter = initHomeParentRVAdapter() Loading Loading @@ -106,19 +99,6 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall } } private fun loadHomePageData() { setupListening() authObjects.observe(viewLifecycleOwner) { if (it == null) return@observe loadDataWhenNetworkAvailable(it) } homeViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { handleExceptionsCommon(it) } } private fun showPaidAppMessage(application: Application) { ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, application.name), Loading @@ -135,66 +115,22 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall ).show(childFragmentManager, "HomeFragment") } override fun onTimeout( exception: Exception, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { if (exception is GPlayException) { setMessage(R.string.timeout_desc_gplay) setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } else { setMessage(R.string.timeout_desc_cleanapk) } setCancelable(false) } } override fun onSignInError( exception: GPlayLoginException, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } } override fun onDataLoadError( exception: Exception, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { if (exception is GPlayException) { setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } } } override fun loadData(authObjectList: List<AuthObject>) { private fun loadData() { if (shouldLoadData()) { showLoadingUI() homeViewModel.loadData(authObjectList, viewLifecycleOwner) { _ -> clearAndRestartGPlayLogin() true } homeViewModel.getHomeScreenData(viewLifecycleOwner) } } private fun shouldLoadData() = homeViewModel.haveSourcesChanged() || !homeViewModel.hasData() override fun showLoadingUI() { private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE } override fun stopLoadingUI() { private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE Loading Loading @@ -281,9 +217,4 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall override fun cancelDownload(app: Application) { mainActivityViewModel.cancelDownload(app) } private fun openSettings() { view?.findNavController() ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_SettingsFragment) } } app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +5 −27 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2025 e Foundation * * 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 Loading @@ -14,6 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.ui.home Loading @@ -21,6 +21,7 @@ package foundation.e.apps.ui.home import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.ResultSupreme Loading @@ -29,10 +30,8 @@ import foundation.e.apps.data.Stores import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.data.Home import foundation.e.apps.data.enums.Source import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.ui.applicationlist.ApplicationDiffUtil import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject Loading @@ -40,7 +39,7 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( private val applicationRepository: ApplicationRepository, private val stores: Stores ) : LoadingViewModel() { ) : ViewModel() { @Inject lateinit var appLoungePreference: AppLoungePreference Loading @@ -61,25 +60,6 @@ class HomeViewModel @Inject constructor( return homeScreenData.value?.data?.isNotEmpty() ?: false } fun loadData( authObjectList: List<AuthObject>, lifecycleOwner: LifecycleOwner, retryBlock: (failedObjects: List<AuthObject>) -> Boolean, ) { super.onLoadData(authObjectList, { successAuthList, _ -> successAuthList.find { it is AuthObject.GPlayAuth }?.run { getHomeScreenData(lifecycleOwner) return@onLoadData } successAuthList.find { it is AuthObject.CleanApk }?.run { getHomeScreenData(lifecycleOwner) return@onLoadData } }, retryBlock) } fun haveSourcesChanged(): Boolean { val newStores = stores.getStores() if (newStores == previousStores) { Loading @@ -90,9 +70,7 @@ class HomeViewModel @Inject constructor( return true } private fun getHomeScreenData( lifecycleOwner: LifecycleOwner, ) { fun getHomeScreenData(lifecycleOwner: LifecycleOwner) { viewModelScope.launch { applicationRepository.getHomeScreenData().observe(lifecycleOwner) { postHomeResult(it) Loading Loading
app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +11 −80 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2025 e Foundation * * 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 Loading @@ -14,29 +13,25 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.ui.home import android.os.Bundle import android.view.View import androidx.appcompat.app.AlertDialog import androidx.fragment.app.Fragment import androidx.fragment.app.activityViewModels import androidx.fragment.app.viewModels import androidx.lifecycle.lifecycleScope import androidx.navigation.findNavController import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.R import foundation.e.apps.data.enums.Status import foundation.e.apps.data.application.ApplicationInstaller import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException import foundation.e.apps.data.enums.Status import foundation.e.apps.databinding.FragmentHomeBinding import foundation.e.apps.di.CommonUtilsModule.safeNavigate import foundation.e.apps.install.download.data.DownloadProgress import foundation.e.apps.install.pkg.PwaManager import foundation.e.apps.ui.AppInfoFetchViewModel Loading @@ -45,14 +40,12 @@ import foundation.e.apps.ui.MainActivityViewModel import foundation.e.apps.ui.application.subFrags.ApplicationDialogFragment import foundation.e.apps.ui.home.model.HomeChildRVAdapter import foundation.e.apps.ui.home.model.HomeParentRVAdapter import foundation.e.apps.ui.parentFragment.TimeoutFragment import kotlinx.coroutines.delay import kotlinx.coroutines.launch import java.util.Locale import javax.inject.Inject @AndroidEntryPoint class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstaller { class HomeFragment : Fragment(R.layout.fragment_home), ApplicationInstaller { /* * Make adapter nullable to avoid memory leaks. Loading @@ -63,7 +56,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall private val binding get() = _binding!! private val homeViewModel: HomeViewModel by viewModels() override val mainActivityViewModel: MainActivityViewModel by activityViewModels() val mainActivityViewModel: MainActivityViewModel by activityViewModels() private val appProgressViewModel: AppProgressViewModel by viewModels() private val appInfoFetchViewModel: AppInfoFetchViewModel by viewModels() Loading @@ -74,7 +67,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall super.onViewCreated(view, savedInstanceState) _binding = FragmentHomeBinding.bind(view) loadHomePageData() loadData() homeParentRVAdapter = initHomeParentRVAdapter() Loading Loading @@ -106,19 +99,6 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall } } private fun loadHomePageData() { setupListening() authObjects.observe(viewLifecycleOwner) { if (it == null) return@observe loadDataWhenNetworkAvailable(it) } homeViewModel.exceptionsLiveData.observe(viewLifecycleOwner) { handleExceptionsCommon(it) } } private fun showPaidAppMessage(application: Application) { ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, application.name), Loading @@ -135,66 +115,22 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall ).show(childFragmentManager, "HomeFragment") } override fun onTimeout( exception: Exception, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { if (exception is GPlayException) { setMessage(R.string.timeout_desc_gplay) setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } else { setMessage(R.string.timeout_desc_cleanapk) } setCancelable(false) } } override fun onSignInError( exception: GPlayLoginException, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } } override fun onDataLoadError( exception: Exception, predefinedDialog: AlertDialog.Builder ): AlertDialog.Builder? { return predefinedDialog.apply { if (exception is GPlayException) { setNegativeButton(R.string.open_settings) { _, _ -> openSettings() } } } } override fun loadData(authObjectList: List<AuthObject>) { private fun loadData() { if (shouldLoadData()) { showLoadingUI() homeViewModel.loadData(authObjectList, viewLifecycleOwner) { _ -> clearAndRestartGPlayLogin() true } homeViewModel.getHomeScreenData(viewLifecycleOwner) } } private fun shouldLoadData() = homeViewModel.haveSourcesChanged() || !homeViewModel.hasData() override fun showLoadingUI() { private fun showLoadingUI() { binding.shimmerLayout.startShimmer() binding.shimmerLayout.visibility = View.VISIBLE binding.parentRV.visibility = View.GONE } override fun stopLoadingUI() { private fun stopLoadingUI() { binding.shimmerLayout.stopShimmer() binding.shimmerLayout.visibility = View.GONE binding.parentRV.visibility = View.VISIBLE Loading Loading @@ -281,9 +217,4 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall override fun cancelDownload(app: Application) { mainActivityViewModel.cancelDownload(app) } private fun openSettings() { view?.findNavController() ?.safeNavigate(R.id.homeFragment, R.id.action_homeFragment_to_SettingsFragment) } }
app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +5 −27 Original line number Diff line number Diff line /* * Apps Quickly and easily install Android apps onto your device! * Copyright (C) 2021 E FOUNDATION * Copyright (C) 2021-2025 e Foundation * * 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 Loading @@ -14,6 +13,7 @@ * * You should have received a copy of the GNU General Public License * along with this program. If not, see <https://www.gnu.org/licenses/>. * */ package foundation.e.apps.ui.home Loading @@ -21,6 +21,7 @@ package foundation.e.apps.ui.home import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.ResultSupreme Loading @@ -29,10 +30,8 @@ import foundation.e.apps.data.Stores import foundation.e.apps.data.application.ApplicationRepository import foundation.e.apps.data.application.data.Home import foundation.e.apps.data.enums.Source import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.preference.AppLoungePreference import foundation.e.apps.ui.applicationlist.ApplicationDiffUtil import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch import javax.inject.Inject Loading @@ -40,7 +39,7 @@ import javax.inject.Inject class HomeViewModel @Inject constructor( private val applicationRepository: ApplicationRepository, private val stores: Stores ) : LoadingViewModel() { ) : ViewModel() { @Inject lateinit var appLoungePreference: AppLoungePreference Loading @@ -61,25 +60,6 @@ class HomeViewModel @Inject constructor( return homeScreenData.value?.data?.isNotEmpty() ?: false } fun loadData( authObjectList: List<AuthObject>, lifecycleOwner: LifecycleOwner, retryBlock: (failedObjects: List<AuthObject>) -> Boolean, ) { super.onLoadData(authObjectList, { successAuthList, _ -> successAuthList.find { it is AuthObject.GPlayAuth }?.run { getHomeScreenData(lifecycleOwner) return@onLoadData } successAuthList.find { it is AuthObject.CleanApk }?.run { getHomeScreenData(lifecycleOwner) return@onLoadData } }, retryBlock) } fun haveSourcesChanged(): Boolean { val newStores = stores.getStores() if (newStores == previousStores) { Loading @@ -90,9 +70,7 @@ class HomeViewModel @Inject constructor( return true } private fun getHomeScreenData( lifecycleOwner: LifecycleOwner, ) { fun getHomeScreenData(lifecycleOwner: LifecycleOwner) { viewModelScope.launch { applicationRepository.getHomeScreenData().observe(lifecycleOwner) { postHomeResult(it) Loading