diff --git a/app/src/main/java/foundation/e/apps/MainActivity.kt b/app/src/main/java/foundation/e/apps/MainActivity.kt index 5b94937da6fd831fd324523609f4c110a596b53b..a0042b28389b5c6b609d8917f576ae435568840e 100644 --- a/app/src/main/java/foundation/e/apps/MainActivity.kt +++ b/app/src/main/java/foundation/e/apps/MainActivity.kt @@ -40,8 +40,8 @@ import com.google.android.material.snackbar.Snackbar import dagger.hilt.android.AndroidEntryPoint import foundation.e.apps.data.fusedDownload.models.FusedDownload import foundation.e.apps.data.login.AuthObject -import foundation.e.apps.data.login.PlayStoreAuthenticator import foundation.e.apps.data.login.LoginViewModel +import foundation.e.apps.data.login.PlayStoreAuthenticator import foundation.e.apps.data.login.exceptions.GPlayValidationException import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.databinding.ActivityMainBinding @@ -105,7 +105,7 @@ class MainActivity : AppCompatActivity() { } } - viewModel.setupConnectivityManager(this) + viewModel.setupConnectivityManager(this.applicationContext) viewModel.internetConnection.observe(this) { isInternetAvailable -> hasInternet = isInternetAvailable diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt index 0c821f28dc4a4bd48c95c4089eef7b1f6d4a1959..7b81cc960fa4b5885125dc41e63ecb7fc8843502 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationApi.kt @@ -141,14 +141,6 @@ interface ApplicationApi { */ fun getFusedAppInstallationStatus(application: Application): Status - /** - * @return true, if any change is found, otherwise false - */ - fun isHomeDataUpdated( - newHomeData: List, - oldHomeData: List - ): Boolean - /** * @return returns true if there is changes in data, otherwise false */ diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt index 21d36bc31dfe96cae1af920b5b3b589d5d1ff51d..869611db23798beaa9b113eb4507631db332f5ae 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationApiImpl.kt @@ -27,16 +27,13 @@ import com.aurora.gplayapi.SearchSuggestEntry import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.Artwork import com.aurora.gplayapi.data.models.AuthData -import com.aurora.gplayapi.data.models.Category as GplayapiCategory import com.aurora.gplayapi.data.models.SearchBundle import com.aurora.gplayapi.data.models.StreamCluster import dagger.hilt.android.qualifiers.ApplicationContext import foundation.e.apps.R import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.cleanapk.CleanApkDownloadInfoFetcher -import foundation.e.apps.data.cleanapk.data.app.Application as CleanApkApplication import foundation.e.apps.data.cleanapk.data.categories.Categories -import foundation.e.apps.data.cleanapk.data.home.Home as CleanApkHome import foundation.e.apps.data.cleanapk.data.home.HomeScreen import foundation.e.apps.data.cleanapk.data.search.Search import foundation.e.apps.data.cleanapk.repositories.CleanApkRepository @@ -58,9 +55,9 @@ import foundation.e.apps.data.application.data.Ratings import foundation.e.apps.data.application.utils.CategoryType import foundation.e.apps.data.application.utils.CategoryUtils import foundation.e.apps.data.fusedDownload.models.FusedDownload -import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.data.handleNetworkResult import foundation.e.apps.data.login.AuthObject +import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.data.preference.PreferenceManagerModule import foundation.e.apps.install.pkg.PWAManagerModule import foundation.e.apps.install.pkg.PkgManagerModule @@ -77,6 +74,9 @@ import timber.log.Timber import javax.inject.Inject import javax.inject.Named import javax.inject.Singleton +import com.aurora.gplayapi.data.models.Category as GplayapiCategory +import foundation.e.apps.data.cleanapk.data.app.Application as CleanApkApplication +import foundation.e.apps.data.cleanapk.data.home.Home as CleanApkHome typealias FusedHomeDeferred = Deferred>> @@ -134,9 +134,11 @@ class ApplicationApiImpl @Inject constructor( resultGplay?.await()?.let { emit(it) } + resultOpenSource?.await()?.let { emit(it) } + resultPWA?.await()?.let { emit(it) } @@ -1284,45 +1286,6 @@ class ApplicationApiImpl @Inject constructor( return list } - /** - * @return true, if any change is found, otherwise false - */ - override fun isHomeDataUpdated( - newHomeData: List, - oldHomeData: List - ): Boolean { - if (newHomeData.size != oldHomeData.size) { - return true - } - - oldHomeData.forEach { - val fusedHome = newHomeData[oldHomeData.indexOf(it)] - if (!it.title.contentEquals(fusedHome.title) || areFusedAppsUpdated(it, fusedHome)) { - return true - } - } - return false - } - - private fun areFusedAppsUpdated( - oldHome: Home, - newHome: Home, - ): Boolean { - val fusedAppDiffUtil = HomeChildFusedAppDiffUtil() - if (oldHome.list.size != newHome.list.size) { - return true - } - - oldHome.list.forEach { oldFusedApp -> - val indexOfOldFusedApp = oldHome.list.indexOf(oldFusedApp) - val fusedApp = newHome.list[indexOfOldFusedApp] - if (!fusedAppDiffUtil.areContentsTheSame(oldFusedApp, fusedApp)) { - return true - } - } - return false - } - /** * @return returns true if there is changes in data, otherwise false */ @@ -1350,7 +1313,7 @@ class ApplicationApiImpl @Inject constructor( return@forEach } val currentAppStatus = - pkgManagerModule.getPackageStatus(it.package_name, it.latest_version_code) + getFusedAppInstallationStatus(it) if (it.status != currentAppStatus) { return true } diff --git a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt index e0b876a634ca85d4bc19511d304293fdba6e3a68..ab7d541a8f612a7e1961455ccebfd7069b932c32 100644 --- a/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt +++ b/app/src/main/java/foundation/e/apps/data/application/ApplicationRepository.kt @@ -135,11 +135,6 @@ class ApplicationRepository @Inject constructor(private val applicationAPIImpl: return applicationAPIImpl.getFusedAppInstallationStatus(application) } - fun isHomeDataUpdated( - newHomeData: List, - oldHomeData: List - ) = applicationAPIImpl.isHomeDataUpdated(newHomeData, oldHomeData) - fun isAnyFusedAppUpdated( newApplications: List, oldApplications: List diff --git a/app/src/main/java/foundation/e/apps/data/application/data/Home.kt b/app/src/main/java/foundation/e/apps/data/application/data/Home.kt index 0dbe525f9463887b8ba2a1031b19d147f29a07c8..520f6a3f1199e4f5916c5cb87ff0dc9442a69346 100644 --- a/app/src/main/java/foundation/e/apps/data/application/data/Home.kt +++ b/app/src/main/java/foundation/e/apps/data/application/data/Home.kt @@ -18,8 +18,11 @@ package foundation.e.apps.data.application.data +import java.util.UUID + data class Home( val title: String = String(), val list: List = emptyList(), - var source: String = String() + var source: String = String(), + var id: String = UUID.randomUUID().toString() ) diff --git a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt index 21956d6f676688d09ebfcea142fc992427635d50..fd0a3a74973723433389721c2d20aa91fbd264f7 100644 --- a/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt +++ b/app/src/main/java/foundation/e/apps/install/pkg/PkgManagerModule.kt @@ -56,7 +56,12 @@ class PkgManagerModule @Inject constructor( fun isInstalled(packageName: String): Boolean { return try { - packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) { + packageManager.getPackageInfo(packageName, PackageManager.PackageInfoFlags.of(0L)) + } else { + packageManager.getPackageInfo(packageName, PackageManager.GET_META_DATA) + } + true } catch (e: PackageManager.NameNotFoundException) { false diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt index 3d0314d993e85f240d3344587d181c36cfb14815..5615aa3ade8687c8f7362c2376e9f80f67b2605f 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeFragment.kt @@ -29,11 +29,9 @@ 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.ResultSupreme 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.application.data.Home import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.GPlayException import foundation.e.apps.data.login.exceptions.GPlayLoginException @@ -93,10 +91,6 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall return@observe } - if (!isHomeDataUpdated(it)) { - return@observe - } - homeParentRVAdapter?.setData(it.data!!) } } @@ -139,12 +133,6 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall ).show(childFragmentManager, "HomeFragment") } - private fun isHomeDataUpdated(homeScreenResult: ResultSupreme>) = - homeParentRVAdapter?.currentList?.isEmpty() == true || homeViewModel.isHomeDataUpdated( - homeScreenResult.data!!, - homeParentRVAdapter?.currentList as List - ) - override fun onTimeout( exception: Exception, predefinedDialog: AlertDialog.Builder @@ -263,9 +251,7 @@ class HomeFragment : TimeoutFragment(R.layout.fragment_home), ApplicationInstall updateProgressOfDownloadingAppItemViews(homeParentRVAdapter, it) } - if (homeViewModel.isAnyAppInstallStatusChanged(homeParentRVAdapter?.currentList)) { - repostAuthObjects() - } + homeViewModel.checkAnyChangeInAppStatus() } override fun onPause() { diff --git a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt index 517513c03df2a5e7f002d07c227f8cacd13cdd68..c7261da582fc07d87227536f8b46804bf6278f0b 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/HomeViewModel.kt @@ -18,6 +18,7 @@ package foundation.e.apps.ui.home +import androidx.annotation.VisibleForTesting import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.viewModelScope @@ -25,13 +26,14 @@ import com.aurora.gplayapi.data.models.AuthData import dagger.hilt.android.lifecycle.HiltViewModel import foundation.e.apps.data.ResultSupreme import foundation.e.apps.data.application.ApplicationRepository -import foundation.e.apps.data.application.data.Application import foundation.e.apps.data.application.data.Home import foundation.e.apps.data.login.AuthObject import foundation.e.apps.data.login.exceptions.CleanApkException import foundation.e.apps.data.login.exceptions.GPlayException +import foundation.e.apps.ui.home.model.HomeChildFusedAppDiffUtil import foundation.e.apps.ui.parentFragment.LoadingViewModel import kotlinx.coroutines.launch +import java.util.UUID import javax.inject.Inject @HiltViewModel @@ -39,6 +41,7 @@ class HomeViewModel @Inject constructor( private val applicationRepository: ApplicationRepository, ) : LoadingViewModel() { + /* * Hold list of applications, as well as application source type. * Source type may change from user selected preference in case of timeout. @@ -47,6 +50,8 @@ class HomeViewModel @Inject constructor( */ var homeScreenData: MutableLiveData>> = MutableLiveData() + var currentHomes: List? = null + fun loadData( authObjectList: List, lifecycleOwner: LifecycleOwner, @@ -72,9 +77,11 @@ class HomeViewModel @Inject constructor( ) { viewModelScope.launch { applicationRepository.getHomeScreenData(authData).observe(lifecycleOwner) { - homeScreenData.postValue(it) + postHomeResult(it) - if (it.isSuccess()) return@observe + if (it.isSuccess()) { + return@observe + } val exception = if (authData.aasToken.isNotBlank() || authData.authToken.isNotBlank()) @@ -93,18 +100,95 @@ class HomeViewModel @Inject constructor( } } - fun isHomeDataUpdated( - newHomeData: List, - oldHomeData: List - ) = applicationRepository.isHomeDataUpdated(newHomeData, oldHomeData) + private fun postHomeResult(homeResult: ResultSupreme>) { + if (shouldUpdateResult(homeResult)) { + homeScreenData.value = homeResult + currentHomes = homeResult.data + return + } + + homeScreenData.value = ResultSupreme.Error("No change is found in homepage") + } + + private fun shouldUpdateResult(homeResult: ResultSupreme>) = + (homeResult.isSuccess() && hasAnyChange(homeResult.data!!)) || !homeResult.isSuccess() + + @VisibleForTesting + fun hasAnyChange( + newHomes: List, + ) = currentHomes.isNullOrEmpty() || newHomes.size != currentHomes!!.size || compareWithNewData( + newHomes + ) + + private fun compareWithNewData(newHomes: List): Boolean { + currentHomes?.forEach { + val fusedHome = newHomes[currentHomes!!.indexOf(it)] + + if (!it.title.contentEquals(fusedHome.title) || !it.id.contentEquals(fusedHome.id) + || areFusedAppsUpdated(it, fusedHome) + ) { + return true + } + } + + return false + } + + private fun areFusedAppsUpdated( + oldHome: Home, + newHome: Home, + ) = oldHome.list.size != newHome.list.size || hasAppListsAnyChange(oldHome, newHome) + + private fun hasAppListsAnyChange( + oldHome: Home, + newHome: Home, + ): Boolean { + val fusedAppDiffUtil = HomeChildFusedAppDiffUtil() + + oldHome.list.forEach { oldFusedApp -> + val indexOfOldFusedApp = oldHome.list.indexOf(oldFusedApp) + val fusedApp = newHome.list[indexOfOldFusedApp] + + if (!fusedAppDiffUtil.areContentsTheSame(oldFusedApp, fusedApp)) { + return true + } + } + + return false + } + + fun checkAnyChangeInAppStatus() { + if (this.currentHomes == null) { + return + } - fun isAnyAppInstallStatusChanged(currentList: List?): Boolean { - if (currentList == null) { - return false + val fusedHomes: MutableList = mutableListOf() + checkForChangesInAppStatus(fusedHomes) + + if (fusedHomes.isNotEmpty() && hasAnyChange(fusedHomes)) { + homeScreenData.value = ResultSupreme.Success(fusedHomes) + currentHomes = fusedHomes } + } + + private fun checkForChangesInAppStatus(fusedHomes: MutableList) { + var home: Home? = null + this.currentHomes?.forEach { + + it.list.forEach { application -> + val status = + applicationRepository.getFusedAppInstallationStatus(application) - val appList = mutableListOf() - currentList.forEach { appList.addAll(it.list) } - return applicationRepository.isAnyAppInstallStatusChanged(appList) + if (application.status != status) { + application.status = status + home = it.copy() + // Setting a new id, so that recyclerview can find that this item is changed + home?.id = UUID.randomUUID().toString() + } + } + + fusedHomes.add(home ?: it) + home = null + } } } diff --git a/app/src/main/java/foundation/e/apps/ui/home/model/FusedHomeDiffUtil.kt b/app/src/main/java/foundation/e/apps/ui/home/model/FusedHomeDiffUtil.kt index 8e05f40ad326fa7b39a7f4a42b04c4e96261a285..a99a867830921443579be21f61a9f707705b0a6b 100644 --- a/app/src/main/java/foundation/e/apps/ui/home/model/FusedHomeDiffUtil.kt +++ b/app/src/main/java/foundation/e/apps/ui/home/model/FusedHomeDiffUtil.kt @@ -27,6 +27,7 @@ class FusedHomeDiffUtil : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: Home, newItem: Home): Boolean { return oldItem.title.contentEquals(newItem.title) && - oldItem.list == newItem.list + oldItem.list == newItem.list && + oldItem.id == newItem.id } } diff --git a/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt b/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt index e636cc92fb79930234cad74342b3019d52970f2d..165af4f725a76f4ed16cd00df98d55ce49d92b47 100644 --- a/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt +++ b/app/src/test/java/foundation/e/apps/fused/ApplicationApiImplTest.kt @@ -36,7 +36,6 @@ import foundation.e.apps.data.enums.ResultStatus import foundation.e.apps.data.enums.Status import foundation.e.apps.data.application.ApplicationApiImpl import foundation.e.apps.data.application.data.Application -import foundation.e.apps.data.application.data.Home import foundation.e.apps.data.application.utils.CategoryType import foundation.e.apps.data.playstore.PlayStoreRepository import foundation.e.apps.install.pkg.PWAManagerModule @@ -340,90 +339,6 @@ class ApplicationApiImplTest { assertFalse("hasInstallStatusUpdated", isAppStatusUpdated) } - @Test - fun isHomeDataUpdated() { - val oldAppList = mutableListOf( - Application( - _id = "111", - status = Status.INSTALLATION_ISSUE, - name = "Demo One", - package_name = "foundation.e.demoone", - latest_version_code = 123 - ), - Application( - _id = "112", - status = Status.INSTALLED, - name = "Demo Two", - package_name = "foundation.e.demotwo", - latest_version_code = 123 - ), - Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "foundation.e.demothree", - latest_version_code = 123 - ) - ) - - val newAppList = mutableListOf( - Application( - _id = "111", - status = Status.INSTALLATION_ISSUE, - name = "Demo One", - package_name = "foundation.e.demoone", - latest_version_code = 123 - ), - Application( - _id = "112", - status = Status.UNAVAILABLE, - name = "Demo Two", - package_name = "foundation.e.demotwo", - latest_version_code = 123 - ), - Application( - _id = "113", - status = Status.UNAVAILABLE, - name = "Demo Three", - package_name = "foundation.e.demothree", - latest_version_code = 123 - ) - ) - - val oldHomeData = - listOf(Home("Top Free Apps", oldAppList), Home("Top Free Games", oldAppList)) - var newHomeData = - listOf(Home("Top Free Apps", oldAppList), Home("Top Free Games", oldAppList)) - var isHomeDataUpdated = fusedAPIImpl.isHomeDataUpdated(newHomeData, oldHomeData) - assertFalse("isHomeDataUpdated/NO", isHomeDataUpdated) - newHomeData = - listOf(Home("Top Free Apps", oldAppList), Home("Top Free Games", newAppList)) - - isHomeDataUpdated = fusedAPIImpl.isHomeDataUpdated(newHomeData, oldHomeData) - assertTrue("isHomeDataUpdated/YES", isHomeDataUpdated) - } - - @Test - fun isHomeDataUpdatedWhenBothAreEmpty() { - val oldHomeData = listOf() - val newHomeData = listOf() - val isHomeDataUpdated = fusedAPIImpl.isHomeDataUpdated(oldHomeData, newHomeData) - assertFalse("isHomeDataUpdated", isHomeDataUpdated) - } - - @Test - fun `is home data updated when fusedapp list size is not same`() { - val oldAppList = mutableListOf(Application(), Application(), Application()) - val newAppList = mutableListOf(Application(), Application()) - - val oldHomeData = - listOf(Home("Top Free Apps", oldAppList), Home("Top Free Games", oldAppList)) - var newHomeData = - listOf(Home("Top Free Apps", oldAppList), Home("Top Free Games", newAppList)) - - val isHomeDataUpdated = fusedAPIImpl.isHomeDataUpdated(newHomeData, oldHomeData) - assertTrue("isHomeDataUpdated/YES", isHomeDataUpdated) - } @Test fun getFusedAppInstallationStatusWhenPWA() { diff --git a/app/src/test/java/foundation/e/apps/home/HomeViewModelTest.kt b/app/src/test/java/foundation/e/apps/home/HomeViewModelTest.kt new file mode 100644 index 0000000000000000000000000000000000000000..7930a3b4841a427b614b5d372b0bd14a27140580 --- /dev/null +++ b/app/src/test/java/foundation/e/apps/home/HomeViewModelTest.kt @@ -0,0 +1,92 @@ +/* + * Copyright MURENA SAS 2023 + * Apps Quickly and easily install Android apps onto your device! + * + * 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.home + +import foundation.e.apps.data.application.ApplicationRepository +import foundation.e.apps.data.application.data.Application +import foundation.e.apps.data.application.data.Home +import foundation.e.apps.data.enums.Status +import foundation.e.apps.ui.home.HomeViewModel +import org.junit.Assert.assertFalse +import org.junit.Before +import org.junit.Test +import org.mockito.Mock +import org.mockito.MockitoAnnotations + +class HomeViewModelTest { + + @Mock + private lateinit var applicationRepository: ApplicationRepository + + private lateinit var homeViewModel: HomeViewModel + + @Before + fun setup() { + MockitoAnnotations.openMocks(this) + homeViewModel = HomeViewModel(applicationRepository) + } + + @Test + fun `test hasAnyChange when app list sizes are not same`() { + val oldAppList = mutableListOf(Application("123"), Application("124"), Application("125")) + val newAppList = mutableListOf(Application("123"), Application("124")) + + val oldHomeData = + listOf(Home("Top Free Apps", oldAppList, id = "123"), Home("Top Free Games", oldAppList, id = "124")) + var newHomeData = + listOf(Home("Top Free Apps", newAppList, id = "123"), Home("Top Free Games", newAppList, id = "124")) + + homeViewModel.currentHomes = oldHomeData + + val hasAnyChange = homeViewModel.hasAnyChange(newHomeData) + assert(hasAnyChange) + } + + @Test + fun `test hasAnyChange when contents are same`() { + val oldAppList = mutableListOf(Application("123"), Application("124"), Application("125")) + val newAppList = mutableListOf(Application("123"), Application("124"), Application("125")) + + val oldHomeData = + listOf(Home("Top Free Apps", oldAppList, id = "123"), Home("Top Free Games", oldAppList, id = "124")) + var newHomeData = + listOf(Home("Top Free Apps", newAppList, id = "123"), Home("Top Free Games", newAppList, id = "124")) + + homeViewModel.currentHomes = oldHomeData + + val hasAnyChange = homeViewModel.hasAnyChange(newHomeData) + assertFalse(hasAnyChange) + } + + @Test + fun `test hasAnyChange when contents are not same`() { + val oldAppList = mutableListOf(Application("123"), Application("124"), Application("125")) + val newAppList = mutableListOf(Application("123"), Application("124", status = Status.INSTALLED), Application("125")) + + val oldHomeData = + listOf(Home("Top Free Apps", oldAppList, id = "123"), Home("Top Free Games", oldAppList, id = "124")) + var newHomeData = + listOf(Home("Top Free Apps", newAppList, id = "123"), Home("Top Free Games", newAppList, id = "124")) + + homeViewModel.currentHomes = oldHomeData + + val hasAnyChange = homeViewModel.hasAnyChange(newHomeData) + assert(hasAnyChange) + } +} \ No newline at end of file