From 9e8c57bb2e608eb36157060619630b3bd65dbd11 Mon Sep 17 00:00:00 2001 From: hasibprince Date: Tue, 1 Nov 2022 08:29:02 +0600 Subject: [PATCH 1/7] fixed: update all flickering --- .../api/faultyApps/FaultyAppRepository.kt | 2 + .../e/apps/updates/UpdatesFragment.kt | 88 ++-- .../e/apps/updates/UpdatesViewModel.kt | 6 +- app/src/main/res/layout/fragment_updates.xml | 1 + .../foundation/e/apps/FakeFaultyAppDao.kt | 23 ++ .../e/apps/UpdateManagerImptTest.kt | 381 ++++++++++++++++++ 6 files changed, 458 insertions(+), 43 deletions(-) create mode 100644 app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt create mode 100644 app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt diff --git a/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt index b1c826009..479460fb7 100644 --- a/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt +++ b/app/src/main/java/foundation/e/apps/api/faultyApps/FaultyAppRepository.kt @@ -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) { diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index e92b909a1..9e1db48f3 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -61,6 +61,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 @@ -123,48 +124,57 @@ 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) { + if (!it.first.isNullOrEmpty()) { + binding.button.isEnabled = true + binding.noUpdates.visibility = View.GONE + } else { + binding.noUpdates.visibility = View.VISIBLE + binding.button.isEnabled = false + } observeDownloadList() isDownloadObserverAdded = true } stopLoadingUI() - if (!it.first.isNullOrEmpty()) { - binding.button.isEnabled = true - binding.noUpdates.visibility = View.GONE - } else { - binding.noUpdates.visibility = View.VISIBLE - binding.button.isEnabled = false - } - - WorkManager.getInstance(requireContext()) - .getWorkInfosForUniqueWorkLiveData(INSTALL_WORK_NAME) - .observe(viewLifecycleOwner) { workInfoList -> - lifecycleScope.launchWhenResumed { - binding.button.isEnabled = !( - it.first.isNullOrEmpty() || - updatesViewModel.checkWorkInfoListHasAnyUpdatableWork( - workInfoList - ) - ) - } - } + Timber.d("===>> observeupdate list called") 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 observeAppInstallationWork() { + WorkManager.getInstance(requireContext()) + .getWorkInfosForUniqueWorkLiveData(INSTALL_WORK_NAME) + .observe(viewLifecycleOwner) { workInfoList -> + lifecycleScope.launchWhenResumed { + binding.button.isEnabled = + shouldUpdateButtonEnable(workInfoList) } - } + } } + private fun shouldUpdateButtonEnable(workInfoList: MutableList) = + !updatesViewModel.updatesList.value?.first.isNullOrEmpty() + && (workInfoList.isNullOrEmpty() + || (!updatesViewModel.checkWorkInfoListHasAnyUpdatableWork(workInfoList) + && updatesViewModel.hasAnyUpdatableApp())) + private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> when (event.data) { @@ -197,9 +207,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 = { @@ -215,8 +223,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte displayTimeoutAlertDialog( timeoutFragment = this, activity = requireActivity(), - message = - if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + message = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.timeout_desc_gplay) } else { getString(R.string.timeout_desc_cleanapk) @@ -227,8 +234,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte resetTimeoutDialogLock() mainActivityViewModel.retryFetchingTokenAfterTimeout() }, - negativeButtonText = - if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { + negativeButtonText = if (updatesViewModel.getApplicationCategoryPreference() == FusedAPIImpl.APP_TYPE_ANY) { getString(R.string.open_settings) } else null, negativeButtonBlock = { @@ -253,14 +259,13 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkManager.getInstance(requireContext()) .getWorkInfosByTagLiveData(UpdatesWorkManager.UPDATES_WORK_NAME) .observe(viewLifecycleOwner) { - val errorStates = - listOf( - WorkInfo.State.FAILED, - WorkInfo.State.BLOCKED, - WorkInfo.State.CANCELLED, - WorkInfo.State.SUCCEEDED - ) - if (!it.isNullOrEmpty() && errorStates.contains(it.last().state)) { + val errorStates = listOf( + WorkInfo.State.FAILED, + WorkInfo.State.BLOCKED, + WorkInfo.State.CANCELLED, + WorkInfo.State.SUCCEEDED + ) + if (!it.isNullOrEmpty() && errorStates.contains(it.last().state) && updatesViewModel.hasAnyUpdatableApp()) { binding.button.isEnabled = true } } @@ -297,8 +302,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } private fun updateProgressOfDownloadingItems( - recyclerView: RecyclerView, - downloadProgress: DownloadProgress + recyclerView: RecyclerView, downloadProgress: DownloadProgress ) { val adapter = recyclerView.adapter as ApplicationListRVAdapter lifecycleScope.launch { diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt index fe1c036fa..8f85b0c8a 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt @@ -47,7 +47,7 @@ class UpdatesViewModel @Inject constructor( } } - suspend fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List): Boolean { + fun checkWorkInfoListHasAnyUpdatableWork(workInfoList: List): Boolean { workInfoList.forEach { workInfo -> if (listOf( WorkInfo.State.ENQUEUED, @@ -75,4 +75,8 @@ class UpdatesViewModel @Inject constructor( fun getApplicationCategoryPreference(): String { return updatesManagerRepository.getApplicationCategoryPreference() } + + fun hasAnyUpdatableApp(): Boolean { + return updatesList.value?.first?.any { it.status == Status.UPDATABLE } == true + } } diff --git a/app/src/main/res/layout/fragment_updates.xml b/app/src/main/res/layout/fragment_updates.xml index 5d701bcbe..df89d69f3 100644 --- a/app/src/main/res/layout/fragment_updates.xml +++ b/app/src/main/res/layout/fragment_updates.xml @@ -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" /> diff --git a/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt new file mode 100644 index 000000000..68202d197 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt @@ -0,0 +1,23 @@ +package foundation.e.apps + +import foundation.e.apps.api.faultyApps.FaultyApp +import foundation.e.apps.api.faultyApps.FaultyAppDao + +class FakeFaultyAppDao : FaultyAppDao { + + private val faultyAppList = mutableListOf() + + override suspend fun addFaultyApp(faultyApp: FaultyApp): Long { + faultyAppList.add(faultyApp) + return -1 + } + + override suspend fun getFaultyApps(): List { + return faultyAppList + } + + override suspend fun deleteFaultyAppByPackageName(packageName: String): Int { + faultyAppList.removeIf { it.packageName == packageName } + return -1 + } +} \ No newline at end of file diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt new file mode 100644 index 000000000..011c39743 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -0,0 +1,381 @@ +package foundation.e.apps + +import android.content.pm.ApplicationInfo +import androidx.arch.core.executor.testing.InstantTaskExecutorRule +import com.aurora.gplayapi.data.models.AuthData +import foundation.e.apps.api.faultyApps.FaultyAppRepository +import foundation.e.apps.api.fused.FusedAPIRepository +import foundation.e.apps.api.fused.data.FusedApp +import foundation.e.apps.manager.pkg.PkgManagerModule +import foundation.e.apps.updates.manager.UpdatesManagerImpl +import foundation.e.apps.util.MainCoroutineRule +import foundation.e.apps.utils.enums.FilterLevel +import foundation.e.apps.utils.enums.Origin +import foundation.e.apps.utils.enums.ResultStatus +import foundation.e.apps.utils.enums.Status +import kotlinx.coroutines.ExperimentalCoroutinesApi +import kotlinx.coroutines.test.runTest +import org.junit.Assert.* +import org.junit.Before +import org.junit.Rule +import org.junit.Test +import org.mockito.Mock +import org.mockito.Mockito +import org.mockito.MockitoAnnotations +import org.mockito.kotlin.any +import org.mockito.kotlin.eq +import timber.log.Timber + +@OptIn(ExperimentalCoroutinesApi::class) +class UpdateManagerImptTest { + + // Run tasks synchronously + @Rule + @JvmField + val instantExecutorRule = InstantTaskExecutorRule() + + // Sets the main coroutines dispatcher to a TestCoroutineScope for unit testing. + @ExperimentalCoroutinesApi + @get:Rule + var mainCoroutineRule = MainCoroutineRule() + + private lateinit var updatesManagerImpl: UpdatesManagerImpl + + @Mock + private lateinit var pkgManagerModule: PkgManagerModule + + @Mock + private lateinit var fusedAPIRepository: FusedAPIRepository + + private lateinit var faultyAppRepository: FaultyAppRepository + + val authData = AuthData("e@e.email", "AtadyMsIAtadyM") + + val applicationInfo = mutableListOf( + ApplicationInfo().apply { this.packageName = "foundation.e.demoone" }, + ApplicationInfo().apply { this.packageName = "foundation.e.demotwo" }, + ApplicationInfo().apply { this.packageName = "foundation.e.demothree" } + ) + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + faultyAppRepository = FaultyAppRepository(FakeFaultyAppDao()) + updatesManagerImpl = + UpdatesManagerImpl(pkgManagerModule, fusedAPIRepository, faultyAppRepository) + } + + @Test + fun getUpdateWhenUpdateIsAvailable() = runTest { + val gplayApps = mutableListOf( + FusedApp( + _id = "111", + status = Status.UPDATABLE, + name = "Demo One", + package_name = "foundation.e.demoone", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + FusedApp( + _id = "112", + status = Status.INSTALLED, + name = "Demo Two", + package_name = "foundation.e.demotwo", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + ) + + val openSourceApps = mutableListOf( + FusedApp( + _id = "113", + status = Status.UPDATABLE, + name = "Demo Three", + package_name = "foundation.e.demothree", + origin = Origin.CLEANAPK, + filterLevel = FilterLevel.NONE + ) + ) + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertEquals("fetchUpdate", 2, updateResult.first.size) + } + + @Test + fun getUpdateWhenInstalledPackageListIsEmpty() = runTest { + val authData = AuthData("e@e.email", "AtadyMsIAtadyM") + val applicationInfo = mutableListOf() + Mockito.`when`(pkgManagerModule.getAllUserApps()).thenReturn(applicationInfo) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertEquals("fetchUpdate", 0, updateResult.first.size) + } + + @Test + fun getUpdateWhenUpdateIsUnavailable() = runTest { + val gplayApps = mutableListOf( + FusedApp( + _id = "111", + status = Status.INSTALLED, + name = "Demo One", + package_name = "foundation.e.demoone", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + FusedApp( + _id = "112", + status = Status.INSTALLED, + name = "Demo Two", + package_name = "foundation.e.demotwo", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + ) + + val openSourceApps = mutableListOf( + FusedApp( + _id = "113", + status = Status.INSTALLED, + name = "Demo Three", + package_name = "foundation.e.demothree", + origin = Origin.CLEANAPK, + filterLevel = FilterLevel.NONE + ) + ) + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertEquals("fetchUpdate", 0, updateResult.first.size) + } + + @Test + fun getUpdateWhenUpdateHasOnlyForOpenSourceApps() = runTest { + val gplayApps = mutableListOf( + FusedApp( + _id = "111", + status = Status.INSTALLED, + name = "Demo One", + package_name = "foundation.e.demoone", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + FusedApp( + _id = "112", + status = Status.INSTALLED, + name = "Demo Two", + package_name = "foundation.e.demotwo", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + ) + + val openSourceApps = mutableListOf( + FusedApp( + _id = "113", + status = Status.UPDATABLE, + name = "Demo Three", + package_name = "foundation.e.demothree", + origin = Origin.CLEANAPK, + filterLevel = FilterLevel.NONE + ) + ) + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertFalse("fetchupdate", updateResult.first.any { it.origin == Origin.GPLAY }) + } + + @Test + fun getUpdateWhenUpdateHasOnlyForGplayApps() = runTest { + val gplayApps = mutableListOf( + FusedApp( + _id = "111", + status = Status.INSTALLED, + name = "Demo One", + package_name = "foundation.e.demoone", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + FusedApp( + _id = "112", + status = Status.UPDATABLE, + name = "Demo Two", + package_name = "foundation.e.demotwo", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + ) + + val openSourceApps = mutableListOf( + FusedApp( + _id = "113", + status = Status.INSTALLED, + name = "Demo Three", + package_name = "foundation.e.demothree", + origin = Origin.CLEANAPK, + filterLevel = FilterLevel.NONE + ) + ) + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertFalse("fetchupdate", updateResult.first.any { it.origin == Origin.CLEANAPK }) + } + + @Test + fun getUpdateWhenFetchingOpenSourceIsFailed() = runTest { + val gplayApps = mutableListOf( + FusedApp( + _id = "111", + status = Status.INSTALLED, + name = "Demo One", + package_name = "foundation.e.demoone", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + FusedApp( + _id = "112", + status = Status.UPDATABLE, + name = "Demo Two", + package_name = "foundation.e.demotwo", + origin = Origin.GPLAY, + filterLevel = FilterLevel.NONE + ), + ) + + val openSourceApps = mutableListOf() + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.TIMEOUT) + val gplayUpdates = Pair(gplayApps, ResultStatus.OK) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertEquals("fetchupdate", 1, updateResult.first.size) + assertEquals("fetchupdate", ResultStatus.TIMEOUT, updateResult.second) + } + + @Test + fun getUpdateWhenFetchingGplayIsFailed() = runTest { + val gplayApps = mutableListOf() + + val openSourceApps = mutableListOf( + FusedApp( + _id = "113", + status = Status.UPDATABLE, + name = "Demo Three", + package_name = "foundation.e.demothree", + origin = Origin.CLEANAPK, + filterLevel = FilterLevel.NONE + ) + ) + + val appList = gplayApps + openSourceApps + val openSourceUpdates = Pair(openSourceApps, ResultStatus.OK) + val gplayUpdates = Pair(gplayApps, ResultStatus.TIMEOUT) + + setupMockingForFetchingUpdates( + applicationInfo, + appList, + authData, + openSourceUpdates, + gplayUpdates + ) + + val updateResult = updatesManagerImpl.getUpdates(authData) + System.out.println("===> updates: ${updateResult.first.map { it.package_name }}") + + assertEquals("fetchupdate", 1, updateResult.first.size) + assertEquals("fetchupdate", ResultStatus.TIMEOUT, updateResult.second) + } + + private suspend fun setupMockingForFetchingUpdates( + applicationInfo: MutableList, + appList: List, + authData: AuthData, + openSourceUpdates: Pair, ResultStatus>, + gplayUpdates: Pair, ResultStatus> + ) { + Mockito.`when`(pkgManagerModule.getAllUserApps()).thenReturn(applicationInfo) + Mockito.`when`( + fusedAPIRepository.getApplicationDetails( + any(), + eq(authData), + eq(Origin.CLEANAPK) + ) + ).thenReturn(openSourceUpdates) + + Mockito.`when`( + fusedAPIRepository.getApplicationDetails( + any(), + eq(authData), + eq(Origin.GPLAY) + ) + ).thenReturn(gplayUpdates) + } + +} \ No newline at end of file -- GitLab From ca2dc2189e8ffe3666759f68eec403ab1ed91f1f Mon Sep 17 00:00:00 2001 From: hasibprince Date: Wed, 2 Nov 2022 20:53:26 +0600 Subject: [PATCH 2/7] refactoring: updatesFragment and updatesviewmodel --- .../e/apps/updates/UpdatesFragment.kt | 27 ++++++++++--------- .../e/apps/updates/UpdatesViewModel.kt | 11 ++++++++ 2 files changed, 26 insertions(+), 12 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index dcdea45c2..3029fcd8d 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -39,7 +39,6 @@ import foundation.e.apps.MainActivityViewModel import foundation.e.apps.PrivacyInfoViewModel import foundation.e.apps.R import foundation.e.apps.api.ResultSupreme -import foundation.e.apps.api.fused.FusedAPIImpl import foundation.e.apps.api.fused.FusedAPIInterface import foundation.e.apps.api.fused.data.FusedApp import foundation.e.apps.application.subFrags.ApplicationDialogFragment @@ -169,8 +168,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte .getWorkInfosForUniqueWorkLiveData(INSTALL_WORK_NAME) .observe(viewLifecycleOwner) { workInfoList -> lifecycleScope.launchWhenResumed { - binding.button.isEnabled = - shouldUpdateButtonEnable(workInfoList) + binding.button.isEnabled = shouldUpdateButtonEnable(workInfoList) } } } @@ -308,18 +306,23 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkManager.getInstance(requireContext()) .getWorkInfosByTagLiveData(UpdatesWorkManager.UPDATES_WORK_NAME) .observe(viewLifecycleOwner) { - val errorStates = listOf( - WorkInfo.State.FAILED, - WorkInfo.State.BLOCKED, - WorkInfo.State.CANCELLED, - WorkInfo.State.SUCCEEDED - ) - if (!it.isNullOrEmpty() && errorStates.contains(it.last().state) && updatesViewModel.hasAnyUpdatableApp()) { - binding.button.isEnabled = true - } + binding.button.isEnabled = hasAnyPendingUpdates(it) } } + private fun hasAnyPendingUpdates( + workInfoList: MutableList + ): Boolean { + val errorStates = listOf( + WorkInfo.State.FAILED, + WorkInfo.State.BLOCKED, + WorkInfo.State.CANCELLED, + WorkInfo.State.SUCCEEDED + ) + return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) + && updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() + } + override fun showLoadingUI() { binding.button.isEnabled = false binding.noUpdates.visibility = View.GONE diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt index c602d6b22..e67eeb866 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesViewModel.kt @@ -120,4 +120,15 @@ class UpdatesViewModel @Inject constructor( 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 + } } -- GitLab From bfb9c708cbe0f07a9498cf747e72f81c01b2cfb6 Mon Sep 17 00:00:00 2001 From: hasibprince Date: Thu, 3 Nov 2022 10:33:40 +0600 Subject: [PATCH 3/7] refactoring: deaclartion of dialog builder --- .../main/java/foundation/e/apps/updates/UpdatesFragment.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 4b277fc71..35d634200 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -86,14 +86,11 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte private var isDownloadObserverAdded = false - private lateinit var alertDialogBuilder: AlertDialog.Builder - override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) _binding = FragmentUpdatesBinding.bind(view) binding.button.isEnabled = false - alertDialogBuilder = AlertDialog.Builder(requireContext()) setupListening() authObjects.observe(viewLifecycleOwner) { @@ -158,6 +155,7 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte 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) } } -- GitLab From 6745faf44400c041350101b14ff9168912009555 Mon Sep 17 00:00:00 2001 From: hasibprince Date: Thu, 3 Nov 2022 10:37:59 +0600 Subject: [PATCH 4/7] fixed lint issues --- .../e/apps/application/ApplicationFragment.kt | 2 +- .../e/apps/updates/UpdatesFragment.kt | 48 +++++-------------- .../foundation/e/apps/FakeFaultyAppDao.kt | 2 +- .../e/apps/UpdateManagerImptTest.kt | 7 ++- 4 files changed, 17 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt index c575c1464..5fac54cdf 100644 --- a/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt +++ b/app/src/main/java/foundation/e/apps/application/ApplicationFragment.kt @@ -713,7 +713,7 @@ class ApplicationFragment : TimeoutFragment(R.layout.fragment_application) { return } val downloadedSize = "${ - Formatter.formatFileSize(requireContext(), progressResult.second).substringBefore(" MB") + Formatter.formatFileSize(requireContext(), progressResult.second).substringBefore(" MB") }/${Formatter.formatFileSize(requireContext(), progressResult.first)}" val progressPercentage = ((progressResult.second / progressResult.first.toDouble()) * 100f).toInt() diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index 35d634200..b32c23bfd 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -172,10 +172,14 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } private fun shouldUpdateButtonEnable(workInfoList: MutableList) = - !updatesViewModel.updatesList.value?.first.isNullOrEmpty() - && (workInfoList.isNullOrEmpty() - || (!updatesViewModel.checkWorkInfoListHasAnyUpdatableWork(workInfoList) - && updatesViewModel.hasAnyUpdatableApp())) + !updatesViewModel.updatesList.value?.first.isNullOrEmpty() && + ( + workInfoList.isNullOrEmpty() || + ( + !updatesViewModel.checkWorkInfoListHasAnyUpdatableWork(workInfoList) && + updatesViewModel.hasAnyUpdatableApp() + ) + ) private fun handleUpdateEvent(appEvent: AppEvent) { val event = appEvent.data as ResultSupreme.WorkError<*> @@ -219,34 +223,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 @@ -317,8 +293,8 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte WorkInfo.State.CANCELLED, WorkInfo.State.SUCCEEDED ) - return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) - && updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() + return !workInfoList.isNullOrEmpty() && errorStates.contains(workInfoList.last().state) && + updatesViewModel.hasAnyUpdatableApp() && !updatesViewModel.hasAnyPendingAppsForUpdate() } override fun showLoadingUI() { @@ -338,7 +314,6 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte appProgressViewModel.downloadProgress.observe(viewLifecycleOwner) { updateProgressOfDownloadingItems(binding.recyclerView, it) } -// resetTimeoutDialogLock() } private fun observeDownloadList() { @@ -352,7 +327,8 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } private fun updateProgressOfDownloadingItems( - recyclerView: RecyclerView, downloadProgress: DownloadProgress + recyclerView: RecyclerView, + downloadProgress: DownloadProgress ) { val adapter = recyclerView.adapter as ApplicationListRVAdapter lifecycleScope.launch { diff --git a/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt index 68202d197..65ad9afe0 100644 --- a/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt +++ b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt @@ -20,4 +20,4 @@ class FakeFaultyAppDao : FaultyAppDao { faultyAppList.removeIf { it.packageName == packageName } return -1 } -} \ No newline at end of file +} diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index 011c39743..d952760ab 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -15,7 +15,8 @@ import foundation.e.apps.utils.enums.ResultStatus import foundation.e.apps.utils.enums.Status import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.test.runTest -import org.junit.Assert.* +import org.junit.Assert.assertEquals +import org.junit.Assert.assertFalse import org.junit.Before import org.junit.Rule import org.junit.Test @@ -24,7 +25,6 @@ import org.mockito.Mockito import org.mockito.MockitoAnnotations import org.mockito.kotlin.any import org.mockito.kotlin.eq -import timber.log.Timber @OptIn(ExperimentalCoroutinesApi::class) class UpdateManagerImptTest { @@ -377,5 +377,4 @@ class UpdateManagerImptTest { ) ).thenReturn(gplayUpdates) } - -} \ No newline at end of file +} -- GitLab From b97f5eba40ede762a609c08246c9707560a4d937 Mon Sep 17 00:00:00 2001 From: hasibprince Date: Thu, 3 Nov 2022 13:05:04 +0600 Subject: [PATCH 5/7] copyrigh added and unncessary log removed --- .../main/java/foundation/e/apps/MainActivity.kt | 2 +- .../java/foundation/e/apps/FakeFaultyAppDao.kt | 17 +++++++++++++++++ .../foundation/e/apps/UpdateManagerImptTest.kt | 17 +++++++++++++++++ 3 files changed, 35 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 65e994b8d..06c5a2b04 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -73,7 +73,7 @@ class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - Timber.d(">>> onCreate 112") + binding = ActivityMainBinding.inflate(layoutInflater) setContentView(binding.root) diff --git a/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt index 65ad9afe0..776985c83 100644 --- a/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt +++ b/app/src/test/java/foundation/e/apps/FakeFaultyAppDao.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2022 MURENA SAS + * + * 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 foundation.e.apps import foundation.e.apps.api.faultyApps.FaultyApp diff --git a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt index d952760ab..61585e56c 100644 --- a/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt +++ b/app/src/test/java/foundation/e/apps/UpdateManagerImptTest.kt @@ -1,3 +1,20 @@ +/* + * Copyright (C) 2022 MURENA SAS + * + * 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 foundation.e.apps import android.content.pm.ApplicationInfo -- GitLab From cc7dc00e59bbf1fb754e018f0677618c934d3aae Mon Sep 17 00:00:00 2001 From: Fahim Salam Chowdhury Date: Thu, 3 Nov 2022 07:08:28 +0000 Subject: [PATCH 6/7] refactoring: UpdatesFragment --- .../e/apps/updates/UpdatesFragment.kt | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index b32c23bfd..a2013abe5 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -140,16 +140,11 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte updatesViewModel.updatesList.observe(viewLifecycleOwner) { listAdapter?.setData(it.first) if (!isDownloadObserverAdded) { - if (!it.first.isNullOrEmpty()) { - binding.button.isEnabled = true - binding.noUpdates.visibility = View.GONE - } else { - binding.noUpdates.visibility = View.VISIBLE - binding.button.isEnabled = false - } + handleStateNoUpdates(it.first) observeDownloadList() isDownloadObserverAdded = true } + stopLoadingUI() Timber.d("===>> observeupdate list called") @@ -161,6 +156,16 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } } + private fun handleStateNoUpdates(fusedAppList: List?) { + if (!fusedAppList.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) -- GitLab From 87faf85af1577109c770b08b606644d7b7d0ebfb Mon Sep 17 00:00:00 2001 From: Jonathan Klee Date: Fri, 4 Nov 2022 08:32:07 +0000 Subject: [PATCH 7/7] refactoring: parameter name of method. --- .../main/java/foundation/e/apps/updates/UpdatesFragment.kt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt index a2013abe5..80273b8c8 100644 --- a/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt +++ b/app/src/main/java/foundation/e/apps/updates/UpdatesFragment.kt @@ -156,8 +156,8 @@ class UpdatesFragment : TimeoutFragment(R.layout.fragment_updates), FusedAPIInte } } - private fun handleStateNoUpdates(fusedAppList: List?) { - if (!fusedAppList.isNullOrEmpty()) { + private fun handleStateNoUpdates(list: List?) { + if (!list.isNullOrEmpty()) { binding.button.isEnabled = true binding.noUpdates.visibility = View.GONE } else { -- GitLab