Skip to content

Commit

Permalink
Merge branch 'trakt-settings-refactor' into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
UweTrottmann committed Nov 13, 2024
2 parents 754478c + f66f981 commit e738046
Show file tree
Hide file tree
Showing 8 changed files with 299 additions and 333 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ class SgSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, tru
val resultTraktSync = TraktSync(
context, movieTools.get(),
traktSync.get(), progress
).sync(currentTime, isHexagonEnabled)
).sync(isHexagonEnabled)
// don't overwrite failure
if (resultCode == UpdateResult.SUCCESS) {
resultCode = resultTraktSync
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,17 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2017-2024 Uwe Trottmann

package com.battlelancer.seriesguide.sync

import android.content.ContentProviderOperation
import android.content.OperationApplicationException
import androidx.preference.PreferenceManager
import com.battlelancer.seriesguide.movies.database.SgMovieFlags
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Movies
import com.battlelancer.seriesguide.provider.SgRoomDatabase
import com.battlelancer.seriesguide.traktapi.SgTrakt
import com.battlelancer.seriesguide.traktapi.TraktCredentials
import com.battlelancer.seriesguide.traktapi.TraktSettings
import com.battlelancer.seriesguide.util.DBUtils
import com.battlelancer.seriesguide.util.Errors
import com.uwetrottmann.androidutils.AndroidUtils
import com.uwetrottmann.trakt5.entities.BaseMovie
import com.uwetrottmann.trakt5.entities.LastActivityMore
import com.uwetrottmann.trakt5.entities.MovieIds
Expand Down Expand Up @@ -47,31 +44,29 @@ class TraktMovieSync(
* thread.
*/
fun syncLists(activity: LastActivityMore): Boolean {
if (activity.collected_at == null) {
val collectedAt = activity.collected_at
if (collectedAt == null) {
Timber.e("syncLists: null collected_at")
return false
}
if (activity.watchlisted_at == null) {
val watchlistedAt = activity.watchlisted_at
if (watchlistedAt == null) {
Timber.e("syncLists: null watchlisted_at")
return false
}
if (activity.watched_at == null) {
val watchedAt = activity.watched_at
if (watchedAt == null) {
Timber.e("syncLists: null watched_at")
return false
}

val merging = !TraktSettings.hasMergedMovies(context)
if (!merging && !TraktSettings.isMovieListsChanged(
context, activity.collected_at, activity.watchlisted_at, activity.watched_at
)) {
val isInitialSync = TraktSettings.isInitialSyncMovies(context)
if (!isInitialSync
&& !TraktSettings.isMovieListsChanged(context, collectedAt, watchlistedAt, watchedAt)) {
Timber.d("syncLists: no changes")
return true
}

if (!TraktCredentials.get(context).hasCredentials()) {
return false
}

// Download Trakt state.
val collection = downloadCollection() ?: return false
val watchlist = downloadWatchlist() ?: return false
Expand All @@ -97,7 +92,7 @@ class TraktMovieSync(
val playsOnTrakt = watchedWithPlays.remove(tmdbId)
val isWatchedOnTrakt = playsOnTrakt != null

if (merging) {
if (isInitialSync) {
// Mark movie for upload if missing from Trakt collection or watchlist
// or if not watched on Trakt.
// Note: If watches were removed on Trakt in the meanwhile, this would re-add them.
Expand Down Expand Up @@ -177,18 +172,15 @@ class TraktMovieSync(
batch.clear() // release for gc

// merge on first run
if (merging) {
if (isInitialSync) {
// Upload movies not in Trakt collection, watchlist or watched history.
if (uploadFlagsNotOnTrakt(
toCollectOnTrakt,
toWatchlistOnTrakt,
toSetWatchedOnTrakt
)) {
// set merge successful
PreferenceManager.getDefaultSharedPreferences(context)
.edit()
.putBoolean(TraktSettings.KEY_HAS_MERGED_MOVIES, true)
.apply()
TraktSettings.setInitialSyncMoviesCompleted(context)
} else {
return false
}
Expand All @@ -202,9 +194,9 @@ class TraktMovieSync(
// store last activity timestamps
TraktSettings.storeLastMoviesChangedAt(
context,
activity.collected_at,
activity.watchlisted_at,
activity.watched_at
collectedAt,
watchlistedAt,
watchedAt
)
// if movies were added, ensure ratings for them are downloaded next
if (collection.isNotEmpty() || watchlist.isNotEmpty() || watchedWithPlays.isNotEmpty()) {
Expand Down Expand Up @@ -321,10 +313,6 @@ class TraktMovieSync(
return true
}

if (!AndroidUtils.isNetworkConnected(context)) {
return false // Fail, no connection is available.
}

// Upload.
var action = ""
val items = SyncItems()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ import androidx.preference.PreferenceManager
import com.battlelancer.seriesguide.provider.SeriesGuideContract.Movies
import com.battlelancer.seriesguide.provider.SgRoomDatabase
import com.battlelancer.seriesguide.traktapi.SgTrakt
import com.battlelancer.seriesguide.traktapi.TraktCredentials
import com.battlelancer.seriesguide.traktapi.TraktSettings
import com.battlelancer.seriesguide.util.DBUtils
import com.battlelancer.seriesguide.util.Errors
Expand Down Expand Up @@ -48,10 +47,6 @@ class TraktRatingsSync(
return true
}

if (!TraktCredentials.get(context).hasCredentials()) {
return false
}

// download rated shows
val ratedShows: List<RatedShow>?
try {
Expand Down Expand Up @@ -133,10 +128,6 @@ class TraktRatingsSync(
return true
}

if (!TraktCredentials.get(context).hasCredentials()) {
return false
}

// download rated episodes
val ratedEpisodes: List<RatedEpisode>?
try {
Expand Down Expand Up @@ -217,10 +208,6 @@ class TraktRatingsSync(
return true
}

if (!TraktCredentials.get(context).hasCredentials()) {
return false
}

// download rated shows
val ratedMovies: List<RatedMovie>?
try {
Expand Down
66 changes: 25 additions & 41 deletions app/src/main/java/com/battlelancer/seriesguide/sync/TraktSync.kt
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2017-2024 Uwe Trottmann

package com.battlelancer.seriesguide.sync

import android.content.Context
import androidx.preference.PreferenceManager
import com.battlelancer.seriesguide.R
import com.battlelancer.seriesguide.SgApp
import com.battlelancer.seriesguide.movies.tools.MovieTools
import com.battlelancer.seriesguide.traktapi.SgTrakt
import com.battlelancer.seriesguide.traktapi.TraktCredentials
import com.battlelancer.seriesguide.traktapi.TraktSettings
import com.battlelancer.seriesguide.traktapi.TraktTools2
import com.battlelancer.seriesguide.util.Errors
Expand All @@ -31,16 +29,26 @@ class TraktSync(
val sync: Sync,
val progress: SyncProgress
) {
private fun noConnection(): Boolean {
return if (AndroidUtils.isNetworkConnected(context)) {
false
} else {
progress.recordError()
true
}
}

/**
* To not conflict with Hexagon sync, can turn on [onlyRatings] so only
* ratings are synced.
*/
fun sync(currentTime: Long, onlyRatings: Boolean): SgSyncAdapter.UpdateResult {
fun sync(onlyRatings: Boolean): SgSyncAdapter.UpdateResult {
progress.publish(SyncProgress.Step.TRAKT)
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
// While responses might get returned from the disk cache,
// this is not desirable when syncing, so frequently check for a network connection.
// Note: looked into creating a separate HTTP client without cache, but it makes sense
// to keep one as some of the responses are re-used in other parts of the app.
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE

// Get last activity timestamps.
val lastActivity = TraktTools2.getLastActivity(context)
Expand All @@ -60,32 +68,23 @@ class TraktSync(
if (!onlyRatings) {
// Download and upload episode watched and collected flags.
progress.publish(SyncProgress.Step.TRAKT_EPISODES)
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
if (!syncEpisodes(tmdbIdsToShowIds, lastActivity.episodes, currentTime)) {
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE
if (!syncEpisodes(tmdbIdsToShowIds, lastActivity.episodes)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
}
// Download episode ratings.
progress.publish(SyncProgress.Step.TRAKT_RATINGS)
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE
if (!ratingsSync.downloadForEpisodes(lastActivity.episodes.rated_at)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}

// SHOWS
// Download show ratings.
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE
if (!ratingsSync.downloadForShows(lastActivity.shows.rated_at)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
Expand All @@ -96,10 +95,7 @@ class TraktSync(
progress.publish(SyncProgress.Step.TRAKT_MOVIES)
// Sync watchlist, collection and watched movies.
if (!onlyRatings) {
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE
if (!TraktMovieSync(this).syncLists(lastActivity.movies)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
Expand All @@ -109,10 +105,7 @@ class TraktSync(
}
// Download movie ratings.
progress.publish(SyncProgress.Step.TRAKT_RATINGS)
if (!AndroidUtils.isNetworkConnected(context)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
}
if (noConnection()) return SgSyncAdapter.UpdateResult.INCOMPLETE
if (!ratingsSync.downloadForMovies(lastActivity.movies.rated_at)) {
progress.recordError()
return SgSyncAdapter.UpdateResult.INCOMPLETE
Expand All @@ -128,17 +121,12 @@ class TraktSync(
*/
private fun syncEpisodes(
tmdbIdsToShowIds: Map<Int, Long>,
lastActivity: LastActivityMore,
currentTime: Long
lastActivity: LastActivityMore
): Boolean {
if (!TraktCredentials.get(context).hasCredentials()) {
return false // Auth was removed.
}

// Download flags.
// If initial sync, upload any flags missing on Trakt
// otherwise clear all local flags not on Trakt.
val isInitialSync = !TraktSettings.hasMergedEpisodes(context)
val isInitialSync = TraktSettings.isInitialSyncEpisodes(context)

// Watched episodes.
val episodeSync = TraktEpisodeSync(this)
Expand All @@ -153,14 +141,10 @@ class TraktSync(
return false
}

val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
if (isInitialSync) {
// Success, set initial sync as complete.
editor.putBoolean(TraktSettings.KEY_HAS_MERGED_EPISODES, true)
TraktSettings.setInitialSyncEpisodesCompleted(context)
}
// Success, set last sync time to now.
editor.putLong(TraktSettings.KEY_LAST_FULL_EPISODE_SYNC, currentTime)
editor.apply()
return true
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
// Copyright 2023 Uwe Trottmann
// SPDX-License-Identifier: Apache-2.0
// Copyright 2021-2024 Uwe Trottmann

package com.battlelancer.seriesguide.traktapi

import android.app.Application
import androidx.core.content.edit
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.viewModelScope
import androidx.preference.PreferenceManager
import com.battlelancer.seriesguide.SgApp
import com.battlelancer.seriesguide.jobs.NetworkJobProcessor
import com.battlelancer.seriesguide.util.Errors
Expand Down Expand Up @@ -97,23 +95,8 @@ class TraktAuthActivityModel(application: Application) : AndroidViewModel(applic
}

// reset sync state before hasCredentials may return true
NetworkJobProcessor(getApplication())
.removeObsoleteJobs(false)
PreferenceManager.getDefaultSharedPreferences(getApplication()).edit {
// make next sync merge local watched and collected episodes with those on trakt
putBoolean(TraktSettings.KEY_HAS_MERGED_EPISODES, false)
// make next sync merge local movies with those on trakt
putBoolean(TraktSettings.KEY_HAS_MERGED_MOVIES, false)

// make sure the next sync will run a full episode sync
putLong(TraktSettings.KEY_LAST_FULL_EPISODE_SYNC, 0)
// make sure the next sync will download all watched movies
putLong(TraktSettings.KEY_LAST_MOVIES_WATCHED_AT, 0)
// make sure the next sync will download all ratings
putLong(TraktSettings.KEY_LAST_SHOWS_RATED_AT, 0)
putLong(TraktSettings.KEY_LAST_EPISODES_RATED_AT, 0)
putLong(TraktSettings.KEY_LAST_MOVIES_RATED_AT, 0)
}
NetworkJobProcessor(getApplication()).removeObsoleteJobs(false)
TraktSettings.resetToInitialSync(getApplication())

// store the access token, refresh token and expiry time
TraktCredentials.get(getApplication()).storeAccessToken(accessToken)
Expand Down
Loading

0 comments on commit e738046

Please sign in to comment.