diff --git a/app/src/main/java/by/alexandr7035/gitstat/core/DataSyncStatus.kt b/app/src/main/java/by/alexandr7035/gitstat/core/DataSyncStatus.kt index a0949e0b..a798779e 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/core/DataSyncStatus.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/core/DataSyncStatus.kt @@ -6,8 +6,5 @@ enum class DataSyncStatus { PENDING_CONTRIBUTIONS, SUCCESS, FAILED_NETWORK, - // TODO delete old - FAILED_NETWORK_WITH_CACHE, - FAILED_NETWORK_WITH_NO_CACHE, AUTHORIZATION_ERROR } \ No newline at end of file diff --git a/app/src/main/java/by/alexandr7035/gitstat/data/DataSyncRepository.kt b/app/src/main/java/by/alexandr7035/gitstat/data/DataSyncRepository.kt index 6d0f31f8..70bfe585 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/data/DataSyncRepository.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/data/DataSyncRepository.kt @@ -51,10 +51,7 @@ class DataSyncRepository @Inject constructor( val contributionRates = fetchContributionRates(contributionDays) // Write cache - appPreferences.lastSuccessCacheSyncDate = 0 - // Be patient with livedata. This call causes updates with null - // So wrap code in observers wuth null check - db.clearAllTables() + clearCache() Timber.tag("DEBUG_TAG").d("profile $profile") db.getUserDao().insertUser(profile) @@ -200,4 +197,23 @@ class DataSyncRepository @Inject constructor( return appPreferences.lastSuccessCacheSyncDate != 0L } + fun checkIfTokenSaved(): Boolean { + return appPreferences.token != null + } + + fun clearCache() { + // Be patient with livedata. This call causes updates with null + // So wrap code in observers with null check + db.clearAllTables() + appPreferences.lastSuccessCacheSyncDate = 0 + } + + fun clearToken() { + appPreferences.token = null + } + + fun getLastCacheSyncDateText(): String { + return timeHelper.getFullFromUnixDate(appPreferences.lastSuccessCacheSyncDate) + } + } \ No newline at end of file diff --git a/app/src/main/java/by/alexandr7035/gitstat/data/SyncRepository.kt b/app/src/main/java/by/alexandr7035/gitstat/data/SyncRepository.kt deleted file mode 100644 index 2ea3b485..00000000 --- a/app/src/main/java/by/alexandr7035/gitstat/data/SyncRepository.kt +++ /dev/null @@ -1,238 +0,0 @@ -package by.alexandr7035.gitstat.data - -import androidx.lifecycle.MutableLiveData -import by.alexandr7035.gitstat.apollo.* -import by.alexandr7035.gitstat.core.* -import by.alexandr7035.gitstat.data.local.CacheDB -import by.alexandr7035.gitstat.data.local.dao.ContributionsDao -import by.alexandr7035.gitstat.data.local.dao.RepositoriesDao -import by.alexandr7035.gitstat.data.local.dao.UserDao -import by.alexandr7035.gitstat.data.local.model.ContributionDayEntity -import by.alexandr7035.gitstat.data.local.model.ContributionRateEntity -import by.alexandr7035.gitstat.data.local.model.ContributionsRatioEntity -import by.alexandr7035.gitstat.data.local.model.ContributionsYearEntity -import by.alexandr7035.gitstat.data.remote.mappers.* -import com.apollographql.apollo3.ApolloClient -import com.apollographql.apollo3.api.Query -import timber.log.Timber -import javax.inject.Inject -import kotlin.math.round - -class SyncRepository @Inject constructor( - private val apolloClient: ApolloClient, - - private val db: CacheDB, - private val userDao: UserDao, - private val reposDao: RepositoriesDao, - private val contributionsDao: ContributionsDao, - - private val profileMapper: UserRemoteToCacheMapper, - private val repositoriesMapper: RepositoriesRemoteToCacheMapper, - private val contributionsMapper: ContributionsDaysListRemoteToCacheMapper, - private val ratioMapper: ContributionsRatioRemoteToCacheMapper, - private val daysToRatesMapper: ContributionDaysToRatesMapper, - - private val timeHelper: TimeHelper, - private val appPreferences: AppPreferences) { - - fun checkIfTokenSaved(): Boolean { - return appPreferences.token != null - } - - fun clearToken() { - appPreferences.token = null - } - - suspend fun syncAllData(syncLiveData: MutableLiveData) { - - val start = System.currentTimeMillis() - - try { - syncLiveData.postValue(DataSyncStatus.PENDING_PROFILE) - syncProfileData() - - syncLiveData.postValue(DataSyncStatus.PENDING_REPOSITORIES) - syncRepositories() - - syncLiveData.postValue(DataSyncStatus.PENDING_CONTRIBUTIONS) - syncAllContributions() - syncContributionRateData() - - syncLiveData.postValue(DataSyncStatus.SUCCESS) - - // Save sync time to preferences if success - saveSyncTime() - - val end = System.currentTimeMillis() - - Timber.d("sync finished in ${end-start} ms") - } - catch (e: AppError) { - when (e.type) { - ErrorType.FAILED_CONNECTION -> { - if (checkForCache()) { - syncLiveData.postValue(DataSyncStatus.FAILED_NETWORK_WITH_CACHE) - } - else { - syncLiveData.postValue(DataSyncStatus.FAILED_NETWORK_WITH_NO_CACHE) - } - } - - ErrorType.FAILED_AUTHORIZATION -> { - syncLiveData.postValue(DataSyncStatus.AUTHORIZATION_ERROR) - } - - ErrorType.UNKNOWN_ERROR -> { - if (checkForCache()) { - syncLiveData.postValue(DataSyncStatus.FAILED_NETWORK_WITH_CACHE) - } - else { - syncLiveData.postValue(DataSyncStatus.FAILED_NETWORK_WITH_NO_CACHE) - } - } - } - } - } - - - suspend fun clearCache() { - db.clearAllTables() - appPreferences.lastSuccessCacheSyncDate = 0 - } - - - private suspend fun syncAllContributions() { - - val profileCreationDate = performApolloRequest(ProfileCreationDateQuery()).viewer.createdAt as String - - val unixCreationDate = timeHelper.getUnixDateFromISO8601(profileCreationDate) - val creationYear = timeHelper.getYearFromUnixDate(unixCreationDate) - val currentYear = timeHelper.getYearFromUnixDate(System.currentTimeMillis()) - - Timber.d("$creationYear $currentYear") - - val contributionDaysCached = ArrayList() - val contributionsRatioCached = ArrayList() - val contributionYears = ArrayList() - - // Date range more than a year is not allowed in this api - // So we have to deal with multiple requests - for (year in creationYear..currentYear) { - - contributionYears.add(ContributionsYearEntity(year)) - - val contributionsData = getContributionsForDateRange(year) - // Transform apollo result into room cache - val cachedContributionsData = contributionsMapper.transform(contributionsData) - contributionDaysCached.addAll(cachedContributionsData) - - // Sync ratio of total contributions (commits, PRs, etc.) - val ratioData = getContributionsRatioForDateRange(year) - val totalContributions = cachedContributionsData.sumOf { it.count } - contributionsRatioCached.add(ratioMapper.transform(ratioData, totalContributions)) - } - - contributionsDao.clearContributionDays() - contributionsDao.insertContributionDays(contributionDaysCached) - contributionsDao.clearContributionYears() - contributionsDao.insertContributionYearsCache(contributionYears) - contributionsDao.clearContributionsRatios() - contributionsDao.insertContributionsRatios(contributionsRatioCached) - } - - - private suspend fun syncProfileData() { - val data = performApolloRequest(ProfileQuery()) - val cachedProfile = profileMapper.transform(data) - userDao.clearUser() - userDao.insertUser(cachedProfile) - } - - private suspend fun syncRepositories() { - val data = performApolloRequest(RepositoriesQuery()) - val cachedRepositories = repositoriesMapper.transform(data) - reposDao.insertRepositories(cachedRepositories) - } - - - private suspend fun syncContributionRateData() { - - val contributionDays = contributionsDao.getContributionDaysList() - val rates = daysToRatesMapper.transform(contributionDays) - - contributionsDao.clearContributionsYearsWithRatesCache() - contributionsDao.insertContributionRatesCache(rates) - } - - - // Pass null to get contributions for the last year - private suspend fun getContributionsForDateRange(year: Int?): ContributionsQuery.Data { - - return if (year == null) { - performApolloRequest(ContributionsQuery(date_from = null, date_to = null)) - } - else { - val iso8601Year = timeHelper.getDatesRangeForYear_iso8601(year) - performApolloRequest( - ContributionsQuery( - date_from = iso8601Year.startDate, - date_to = iso8601Year.endDate - ) - ) - } - } - - - // TODO dry - private suspend fun getContributionsRatioForDateRange(year: Int?): ContributionsRatioQuery.Data { - - return if (year == null) { - performApolloRequest(ContributionsRatioQuery(date_from = null, date_to = null)) - } - else { - val iso8601Year = timeHelper.getDatesRangeForYear_iso8601(year) - performApolloRequest( - ContributionsRatioQuery( - date_from = iso8601Year.startDate, - date_to = iso8601Year.endDate - )) - } - } - - - // Generic request handling - // Handle network errors here - private suspend fun performApolloRequest(query: Query): D { - val response = apolloClient.query(query) - - if (response.hasErrors()) { - throw AppError(ErrorType.UNKNOWN_ERROR) - } - else { - if (response.data == null) { - throw AppError(ErrorType.UNKNOWN_ERROR) - } - else { - return response.data!! - } - } - } - - fun getLastCacheSyncDateText(): String { - return timeHelper.getFullFromUnixDate(appPreferences.lastSuccessCacheSyncDate) - } - - fun checkForCache(): Boolean { - return appPreferences.lastSuccessCacheSyncDate != 0L - } - - private fun saveSyncTime() { - // TODO check for timezone - appPreferences.lastSuccessCacheSyncDate = System.currentTimeMillis() - } - - private fun clearSyncTime() { - appPreferences.lastSuccessCacheSyncDate = 0 - } - -} \ No newline at end of file diff --git a/app/src/main/java/by/alexandr7035/gitstat/di/AppModule.kt b/app/src/main/java/by/alexandr7035/gitstat/di/AppModule.kt index 25e7e479..9b6629fa 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/di/AppModule.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/di/AppModule.kt @@ -54,24 +54,6 @@ object AppModule { return UserRepository(dao) } - @Provides - @Singleton - fun provideSyncRepository( - apolloClient: ApolloClient, - db: CacheDB, - userDao: UserDao, - contributionsDao: ContributionsDao, - repositoriesDao: RepositoriesDao, - profileMapper: UserRemoteToCacheMapper, - repositoriesMapper: RepositoriesRemoteToCacheMapper, - contributionsMapper: ContributionsDaysListRemoteToCacheMapper, - daysToRatesMapper: ContributionDaysToRatesMapper, - ratioMapper: ContributionsRatioRemoteToCacheMapper, - timeHelper: TimeHelper, - appPreferences: AppPreferences): SyncRepository { - return SyncRepository(apolloClient, db, userDao, repositoriesDao, contributionsDao, profileMapper, repositoriesMapper, contributionsMapper, ratioMapper, daysToRatesMapper, timeHelper, appPreferences) - } - @Provides @Singleton fun provideAuthRepository(apolloClient: ApolloClient, appPreferences: AppPreferences): AuthRepository { diff --git a/app/src/main/java/by/alexandr7035/gitstat/view/MainViewModel.kt b/app/src/main/java/by/alexandr7035/gitstat/view/MainViewModel.kt index cffdfdc1..c9ea8849 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/view/MainViewModel.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/view/MainViewModel.kt @@ -3,17 +3,16 @@ package by.alexandr7035.gitstat.view import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import by.alexandr7035.gitstat.data.DataSyncRepository -import by.alexandr7035.gitstat.data.SyncRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class MainViewModel @Inject constructor(private val syncRepository: SyncRepository, private val dataSyncRepository: DataSyncRepository): ViewModel() { +class MainViewModel @Inject constructor(private val dataSyncRepository: DataSyncRepository): ViewModel() { fun checkIfTokenSaved(): Boolean { - return syncRepository.checkIfTokenSaved() + return dataSyncRepository.checkIfTokenSaved() } fun checkIfCacheExists(): Boolean { @@ -22,15 +21,15 @@ class MainViewModel @Inject constructor(private val syncRepository: SyncReposito fun clearCache() { viewModelScope.launch(Dispatchers.IO) { - syncRepository.clearCache() + dataSyncRepository.clearCache() } } fun getCacheSyncDate(): String { - return syncRepository.getLastCacheSyncDateText() + return dataSyncRepository.getLastCacheSyncDateText() } fun clearToken() { - syncRepository.clearToken() + dataSyncRepository.clearToken() } } \ No newline at end of file diff --git a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNoCacheFragment.kt b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNetworkFragment.kt similarity index 79% rename from app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNoCacheFragment.kt rename to app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNetworkFragment.kt index b9748261..eff6a697 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNoCacheFragment.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncFailedNetworkFragment.kt @@ -7,18 +7,18 @@ import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.navigation.navGraphViewModels import by.alexandr7035.gitstat.R -import by.alexandr7035.gitstat.databinding.FragmentSyncFailedNoCacheBinding +import by.alexandr7035.gitstat.databinding.FragmentSyncFailedNetworkBinding -class SyncFailedNoCacheFragment : Fragment() { +class SyncFailedNetworkFragment : Fragment() { - private var binding: FragmentSyncFailedNoCacheBinding? = null + private var binding: FragmentSyncFailedNetworkBinding? = null private val viewModel by navGraphViewModels(R.id.syncGraph) { defaultViewModelProviderFactory } override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { // Inflate the layout for this fragment - binding = FragmentSyncFailedNoCacheBinding.inflate(inflater, container, false) + binding = FragmentSyncFailedNetworkBinding.inflate(inflater, container, false) return binding!!.root } diff --git a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncHostFragment.kt b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncHostFragment.kt index d6719163..2f1057e7 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncHostFragment.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncHostFragment.kt @@ -59,9 +59,9 @@ class SyncHostFragment : Fragment() { .commit() } - DataSyncStatus.FAILED_NETWORK_WITH_NO_CACHE -> { + DataSyncStatus.FAILED_NETWORK -> { requireActivity().supportFragmentManager.beginTransaction() - .replace(R.id.containerView, SyncFailedNoCacheFragment()) + .replace(R.id.containerView, SyncFailedNetworkFragment()) .commit() } diff --git a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncViewModel.kt b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncViewModel.kt index 87043015..1091a6c4 100644 --- a/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncViewModel.kt +++ b/app/src/main/java/by/alexandr7035/gitstat/view/datasync/SyncViewModel.kt @@ -4,45 +4,23 @@ import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import by.alexandr7035.gitstat.core.DataSyncStatus -import by.alexandr7035.gitstat.data.SyncRepository +import by.alexandr7035.gitstat.data.DataSyncRepository import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel -class SyncViewModel @Inject constructor(private val repository: SyncRepository): ViewModel() { +class SyncViewModel @Inject constructor(private val repository: DataSyncRepository): ViewModel() { private val syncStatusLiveData = MutableLiveData() - fun checkIfTokenSaved(): Boolean { - return repository.checkIfTokenSaved() - } - - fun clearToken() { - repository.clearToken() - } - fun getSyncStatusLiveData(): MutableLiveData { return syncStatusLiveData } fun syncData() { viewModelScope.launch(Dispatchers.IO) { - repository.syncAllData(syncStatusLiveData) - } - } - - fun getLastCacheSyncDate(): String { - return repository.getLastCacheSyncDateText() - } - - fun clearCache() { - viewModelScope.launch(Dispatchers.IO) { - repository.clearCache() + repository.syncData(syncStatusLiveData) } } - - fun checkForCache(): Boolean { - return repository.checkForCache() - } } \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_info_dialog.xml b/app/src/main/res/layout/fragment_info_dialog.xml index b199204f..8b9a4239 100644 --- a/app/src/main/res/layout/fragment_info_dialog.xml +++ b/app/src/main/res/layout/fragment_info_dialog.xml @@ -7,7 +7,7 @@ android:layout_height="wrap_content" android:padding="10dp" android:orientation="vertical" - tools:context=".view.datasync.SyncFailedNoCacheFragment"> + tools:context=".view.datasync.SyncFailedNetworkFragment"> + tools:context=".view.datasync.SyncFailedNetworkFragment"> + tools:context=".view.datasync.SyncFailedNetworkFragment"> + tools:context=".view.datasync.SyncFailedNetworkFragment"> + tools:layout="@layout/fragment_sync_failed_network" />