Loading app/src/main/java/foundation/e/apps/MainActivity.kt +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Timber.d(">>> onCreate") binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) Loading app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -20,11 +20,13 @@ package foundation.e.apps.api.faultyApps import foundation.e.apps.OpenForTesting import foundation.e.apps.api.fused.data.FusedApp import javax.inject.Inject import javax.inject.Singleton @Singleton @OpenForTesting class FaultyAppRepository @Inject constructor(private val faultyAppDao: FaultyAppDao) { suspend fun addFaultyApp(packageName: String, error: String) { Loading app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +62 −86 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import foundation.e.apps.utils.toast import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint Loading Loading @@ -90,21 +91,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte _binding = FragmentUpdatesBinding.bind(view) binding.button.isEnabled = false /* * Explanation of double observers in HomeFragment.kt */ /*mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) { return@observe } refreshDataOrRefreshToken(mainActivityViewModel) } mainActivityViewModel.authData.observe(viewLifecycleOwner) { refreshDataOrRefreshToken(mainActivityViewModel) }*/ setupListening() authObjects.observe(viewLifecycleOwner) { Loading Loading @@ -139,47 +125,66 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte adapter = listAdapter layoutManager = LinearLayoutManager(view.context) } observeAppInstallationWork() observeUpdateList(listAdapter) viewLifecycleOwner.lifecycleScope.launch { EventBus.events.flowWithLifecycle(viewLifecycleOwner.lifecycle) .filter { appEvent -> appEvent is AppEvent.UpdateEvent }.collectLatest { handleUpdateEvent(it) } } } private fun observeUpdateList(listAdapter: ApplicationListRVAdapter?) { updatesViewModel.updatesList.observe(viewLifecycleOwner) { listAdapter?.setData(it.first) if (!isDownloadObserverAdded) { handleStateNoUpdates(it.first) observeDownloadList() isDownloadObserverAdded = true } stopLoadingUI() if (!it.first.isNullOrEmpty()) { Timber.d("===>> observeupdate list called") if (it.second != ResultStatus.OK) { val exception = GPlayException(it.second == ResultStatus.TIMEOUT) val alertDialogBuilder = AlertDialog.Builder(requireContext()) onTimeout(exception, alertDialogBuilder) } } } private fun handleStateNoUpdates(list: List<FusedApp>?) { if (!list.isNullOrEmpty()) { binding.button.isEnabled = true binding.noUpdates.visibility = View.GONE } else { binding.noUpdates.visibility = View.VISIBLE binding.button.isEnabled = false } } private fun observeAppInstallationWork() { WorkManager.getInstance(requireContext()) .getWorkInfosForUniqueWorkLiveData(INSTALL_WORK_NAME) .observe(viewLifecycleOwner) { workInfoList -> lifecycleScope.launchWhenResumed { binding.button.isEnabled = !( it.first.isNullOrEmpty() || updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( workInfoList ) ) binding.button.isEnabled = shouldUpdateButtonEnable(workInfoList) } } /*if (it.second != ResultStatus.OK) { onTimeout() }*/ } viewLifecycleOwner.lifecycleScope.launch { EventBus.events.flowWithLifecycle(viewLifecycleOwner.lifecycle) .filter { appEvent -> appEvent is AppEvent.UpdateEvent }.collectLatest { handleUpdateEvent(it) } } } private fun shouldUpdateButtonEnable(workInfoList: MutableList<WorkInfo>) = !updatesViewModel.updatesList.value?.first.isNullOrEmpty() && ( workInfoList.isNullOrEmpty() || ( !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork(workInfoList) && updatesViewModel.hasAnyUpdatableApp() ) ) private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> Loading Loading @@ -213,9 +218,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, fusedApp.name), message = getString( R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price ), positiveButtonText = getString(R.string.dialog_confirm), positiveButtonAction = { Loading @@ -225,36 +228,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte ).show(childFragmentManager, "UpdatesFragment") } /*override fun onTimeout() { if (!isTimeoutDialogDisplayed()) { stopLoadingUI() displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.timeout_desc_gplay) } else { getString(R.string.timeout_desc_cleanapk) }, positiveButtonText = getString(R.string.retry), positiveButtonBlock = { showLoadingUI() resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.open_settings) } else null, negativeButtonBlock = { openSettings() }, allowCancel = true, ) } }*/ override fun onTimeout( exception: Exception, predefinedDialog: AlertDialog.Builder Loading Loading @@ -312,17 +285,21 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkManager.getInstance(requireContext()) .getWorkInfosByTagLiveData(UpdatesWorkManager.UPDATES_WORK_NAME) .observe(viewLifecycleOwner) { val errorStates = listOf( binding.button.isEnabled = hasAnyPendingUpdates(it) } } private fun hasAnyPendingUpdates( workInfoList: MutableList<WorkInfo> ): Boolean { val errorStates = listOf( WorkInfo.State.FAILED, WorkInfo.State.BLOCKED, WorkInfo.State.CANCELLED, WorkInfo.State.SUCCEEDED ) if (!it.isNullOrEmpty() && errorStates.contains(it.last().state)) { binding.button.isEnabled = true } } return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) && updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() } override fun showLoadingUI() { Loading @@ -342,7 +319,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { updateProgressOfDownloadingItems(binding.recyclerView, it) } // resetTimeoutDialogLock() } private fun observeDownloadList() { Loading app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +16 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ class UpdatesViewModel @Inject constructor( } } suspend fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List<WorkInfo>): Boolean { fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List<WorkInfo>): Boolean { workInfoList.forEach { workInfo -> if (listOf( WorkInfo.State.ENQUEUED, Loading Loading @@ -118,4 +118,19 @@ class UpdatesViewModel @Inject constructor( fun getApplicationCategoryPreference(): String { return updatesManagerRepository.getApplicationCategoryPreference() } fun hasAnyUpdatableApp(): Boolean { return updatesList.value?.first?.any { it.status == Status.UPDATABLE || it.status == Status.INSTALLATION_ISSUE } == true } fun hasAnyPendingAppsForUpdate(): Boolean { val pendingStatesForUpdate = listOf( Status.QUEUED, Status.AWAITING, Status.DOWNLOADING, Status.DOWNLOADED, Status.INSTALLING ) return updatesList.value?.first?.any { pendingStatesForUpdate.contains(it.status) } == true } } app/src/main/res/layout/fragment_updates.xml +1 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ android:textAllCaps="false" android:textSize="16sp" app:autoSizeTextType="uniform" android:enabled="false" app:layout_constraintBottom_toTopOf="@+id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> Loading Loading
app/src/main/java/foundation/e/apps/MainActivity.kt +1 −1 Original line number Diff line number Diff line Loading @@ -73,7 +73,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) Timber.d(">>> onCreate") binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) Loading
app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt +2 −0 Original line number Diff line number Diff line Loading @@ -20,11 +20,13 @@ package foundation.e.apps.api.faultyApps import foundation.e.apps.OpenForTesting import foundation.e.apps.api.fused.data.FusedApp import javax.inject.Inject import javax.inject.Singleton @Singleton @OpenForTesting class FaultyAppRepository @Inject constructor(private val faultyAppDao: FaultyAppDao) { suspend fun addFaultyApp(packageName: String, error: String) { Loading
app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +62 −86 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ import foundation.e.apps.utils.toast import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.filter import kotlinx.coroutines.launch import timber.log.Timber import javax.inject.Inject @AndroidEntryPoint Loading Loading @@ -90,21 +91,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte _binding = FragmentUpdatesBinding.bind(view) binding.button.isEnabled = false /* * Explanation of double observers in HomeFragment.kt */ /*mainActivityViewModel.internetConnection.observe(viewLifecycleOwner) { if (!updatesViewModel.updatesList.value?.first.isNullOrEmpty()) { return@observe } refreshDataOrRefreshToken(mainActivityViewModel) } mainActivityViewModel.authData.observe(viewLifecycleOwner) { refreshDataOrRefreshToken(mainActivityViewModel) }*/ setupListening() authObjects.observe(viewLifecycleOwner) { Loading Loading @@ -139,47 +125,66 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte adapter = listAdapter layoutManager = LinearLayoutManager(view.context) } observeAppInstallationWork() observeUpdateList(listAdapter) viewLifecycleOwner.lifecycleScope.launch { EventBus.events.flowWithLifecycle(viewLifecycleOwner.lifecycle) .filter { appEvent -> appEvent is AppEvent.UpdateEvent }.collectLatest { handleUpdateEvent(it) } } } private fun observeUpdateList(listAdapter: ApplicationListRVAdapter?) { updatesViewModel.updatesList.observe(viewLifecycleOwner) { listAdapter?.setData(it.first) if (!isDownloadObserverAdded) { handleStateNoUpdates(it.first) observeDownloadList() isDownloadObserverAdded = true } stopLoadingUI() if (!it.first.isNullOrEmpty()) { Timber.d("===>> observeupdate list called") if (it.second != ResultStatus.OK) { val exception = GPlayException(it.second == ResultStatus.TIMEOUT) val alertDialogBuilder = AlertDialog.Builder(requireContext()) onTimeout(exception, alertDialogBuilder) } } } private fun handleStateNoUpdates(list: List<FusedApp>?) { if (!list.isNullOrEmpty()) { binding.button.isEnabled = true binding.noUpdates.visibility = View.GONE } else { binding.noUpdates.visibility = View.VISIBLE binding.button.isEnabled = false } } private fun observeAppInstallationWork() { WorkManager.getInstance(requireContext()) .getWorkInfosForUniqueWorkLiveData(INSTALL_WORK_NAME) .observe(viewLifecycleOwner) { workInfoList -> lifecycleScope.launchWhenResumed { binding.button.isEnabled = !( it.first.isNullOrEmpty() || updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( workInfoList ) ) binding.button.isEnabled = shouldUpdateButtonEnable(workInfoList) } } /*if (it.second != ResultStatus.OK) { onTimeout() }*/ } viewLifecycleOwner.lifecycleScope.launch { EventBus.events.flowWithLifecycle(viewLifecycleOwner.lifecycle) .filter { appEvent -> appEvent is AppEvent.UpdateEvent }.collectLatest { handleUpdateEvent(it) } } } private fun shouldUpdateButtonEnable(workInfoList: MutableList<WorkInfo>) = !updatesViewModel.updatesList.value?.first.isNullOrEmpty() && ( workInfoList.isNullOrEmpty() || ( !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork(workInfoList) && updatesViewModel.hasAnyUpdatableApp() ) ) private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> Loading Loading @@ -213,9 +218,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte ApplicationDialogFragment( title = getString(R.string.dialog_title_paid_app, fusedApp.name), message = getString( R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price R.string.dialog_paidapp_message, fusedApp.name, fusedApp.price ), positiveButtonText = getString(R.string.dialog_confirm), positiveButtonAction = { Loading @@ -225,36 +228,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte ).show(childFragmentManager, "UpdatesFragment") } /*override fun onTimeout() { if (!isTimeoutDialogDisplayed()) { stopLoadingUI() displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), message = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.timeout_desc_gplay) } else { getString(R.string.timeout_desc_cleanapk) }, positiveButtonText = getString(R.string.retry), positiveButtonBlock = { showLoadingUI() resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, negativeButtonText = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.open_settings) } else null, negativeButtonBlock = { openSettings() }, allowCancel = true, ) } }*/ override fun onTimeout( exception: Exception, predefinedDialog: AlertDialog.Builder Loading Loading @@ -312,17 +285,21 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkManager.getInstance(requireContext()) .getWorkInfosByTagLiveData(UpdatesWorkManager.UPDATES_WORK_NAME) .observe(viewLifecycleOwner) { val errorStates = listOf( binding.button.isEnabled = hasAnyPendingUpdates(it) } } private fun hasAnyPendingUpdates( workInfoList: MutableList<WorkInfo> ): Boolean { val errorStates = listOf( WorkInfo.State.FAILED, WorkInfo.State.BLOCKED, WorkInfo.State.CANCELLED, WorkInfo.State.SUCCEEDED ) if (!it.isNullOrEmpty() && errorStates.contains(it.last().state)) { binding.button.isEnabled = true } } return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) && updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() } override fun showLoadingUI() { Loading @@ -342,7 +319,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { updateProgressOfDownloadingItems(binding.recyclerView, it) } // resetTimeoutDialogLock() } private fun observeDownloadList() { Loading
app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +16 −1 Original line number Diff line number Diff line Loading @@ -90,7 +90,7 @@ class UpdatesViewModel @Inject constructor( } } suspend fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List<WorkInfo>): Boolean { fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List<WorkInfo>): Boolean { workInfoList.forEach { workInfo -> if (listOf( WorkInfo.State.ENQUEUED, Loading Loading @@ -118,4 +118,19 @@ class UpdatesViewModel @Inject constructor( fun getApplicationCategoryPreference(): String { return updatesManagerRepository.getApplicationCategoryPreference() } fun hasAnyUpdatableApp(): Boolean { return updatesList.value?.first?.any { it.status == Status.UPDATABLE || it.status == Status.INSTALLATION_ISSUE } == true } fun hasAnyPendingAppsForUpdate(): Boolean { val pendingStatesForUpdate = listOf( Status.QUEUED, Status.AWAITING, Status.DOWNLOADING, Status.DOWNLOADED, Status.INSTALLING ) return updatesList.value?.first?.any { pendingStatesForUpdate.contains(it.status) } == true } }
app/src/main/res/layout/fragment_updates.xml +1 −0 Original line number Diff line number Diff line Loading @@ -60,6 +60,7 @@ android:textAllCaps="false" android:textSize="16sp" app:autoSizeTextType="uniform" android:enabled="false" app:layout_constraintBottom_toTopOf="@+id/guideline" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintTop_toTopOf="parent" /> Loading