From 70da17b7b2dbb51d80cc32a3e82095e3b1fd6502 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 11:05:57 +0200 Subject: [PATCH 01/13] ItemAddShowViewHolder: build poster URL before passing to adapters --- .../discover/BaseShowResultsDataSource.kt | 4 +- .../search/discover/ItemAddShowViewHolder.kt | 12 +- .../search/discover/SearchResultMapper.kt | 123 ++++++++++++++++++ .../shows/search/discover/SearchTools.kt | 55 -------- .../search/discover/ShowsDiscoverLiveData.kt | 8 +- .../shows/search/discover/TraktAddLoader.kt | 54 +------- .../search/similar/SimilarShowsViewModel.kt | 8 +- 7 files changed, 139 insertions(+), 125 deletions(-) create mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt delete mode 100644 app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchTools.kt diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseShowResultsDataSource.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseShowResultsDataSource.kt index e01955272b..3662769805 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseShowResultsDataSource.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/BaseShowResultsDataSource.kt @@ -58,8 +58,8 @@ abstract class BaseShowResultsDataSource( nextKey = null ) } else { - val searchResults = SearchTools.mapTvShowsToSearchResults(languageCode, shows) - SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults) + val searchResults = TmdbSearchResultMapper(context, languageCode) + .mapToSearchResults(shows) LoadResult.Page( data = searchResults, prevKey = null, // Only paging forward. diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt index 5ec1a1a207..09c510fde7 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt @@ -73,18 +73,10 @@ class ItemAddShowViewHolder( isVisible = true } - // image - // If item is provided from Trakt source, it does not provide images, - // so need to resolve them. - val posterUrl = ImageTools.posterUrlOrResolve( - item.posterPath, - item.tmdbId, - item.language, - context - ) + // poster ImageTools.loadShowPosterUrlResizeCrop( context, binding.imageViewAddPoster, - posterUrl + item.posterPath // actually the poster URL ) // context/long press listener and more options button diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt new file mode 100644 index 0000000000..17be8dd2a7 --- /dev/null +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt @@ -0,0 +1,123 @@ +// SPDX-License-Identifier: Apache-2.0 +// Copyright 2019-2024 Uwe Trottmann + +package com.battlelancer.seriesguide.shows.search.discover + +import android.content.Context +import com.battlelancer.seriesguide.SgApp +import com.battlelancer.seriesguide.util.ImageTools +import com.uwetrottmann.tmdb2.entities.BaseTvShow +import com.uwetrottmann.trakt5.entities.BaseShow + +/** + * See [mapToSearchResults]. + */ +abstract class SearchResultMapper( + private val context: Context +) { + + /** + * Maps to a list of [SearchResult] with [mapToSearchResult]. + * + * For added shows, changes [SearchResult.state] to [SearchResult.STATE_ADDED] and if available + * uses the poster path of that show (which might differ if the added show is set to a different + * language). + * + * Builds the final poster URL with [buildPosterUrl]. + */ + fun mapToSearchResults(shows: List): List { + val localShowsToPoster = + SgApp.getServicesComponent(context).showTools().getTmdbIdsToPoster() + return shows.mapNotNull { show -> + val searchResult = mapToSearchResult(show) + ?: return@mapNotNull null + + if (localShowsToPoster.indexOfKey(searchResult.tmdbId) >= 0) { + // Is already in local database. + searchResult.state = SearchResult.STATE_ADDED + // Use the poster already fetched for it. + val posterOrNull = localShowsToPoster[searchResult.tmdbId] + if (posterOrNull != null) { + searchResult.posterPath = posterOrNull + } + } + + // It may take some time to build the image cache URL, so do this here instead of when + // binding to the view. + searchResult.posterPath = buildPosterUrl(searchResult) + + searchResult + } + } + + abstract fun mapToSearchResult(show: SHOW): SearchResult? + + abstract fun buildPosterUrl(searchResult: SearchResult): String? + +} + +class TmdbSearchResultMapper( + private val context: Context, + private val languageCode: String +) : SearchResultMapper(context) { + + override fun mapToSearchResult(show: BaseTvShow): SearchResult? { + val tmdbId = show.id ?: return null + return SearchResult().also { + it.tmdbId = tmdbId + it.title = show.name + it.overview = show.overview + it.language = languageCode + it.posterPath = show.poster_path + } + } + + override fun buildPosterUrl(searchResult: SearchResult): String? { + return ImageTools.tmdbOrTvdbPosterUrl(searchResult.posterPath, context) + } + +} + +/** + * Maps Trakt shows to a list of [SearchResult]. + */ +class TraktSearchResultMapper( + private val context: Context, + private val languageCode: String +) : SearchResultMapper(context) { + + override fun mapToSearchResult(show: BaseShow): SearchResult? { + val traktShow = show.show + val tmdbId = traktShow?.ids?.tmdb + ?: return null // has no TMDB id + return SearchResult().also { + it.tmdbId = tmdbId + it.title = traktShow.title + // Trakt might not return an overview, so use the year if available + it.overview = if (!traktShow.overview.isNullOrEmpty()) { + traktShow.overview + } else if (traktShow.year != null) { + traktShow.year!!.toString() + } else { + "" + } + it.language = languageCode + } + } + + /** + * Uses [ImageTools.posterUrlOrResolve] to build the final poster URL or a special resolve URL. + * + * Trakt does not return posters, so sets a special URL that makes the image loader resolve + * them. But for added shows uses their TMDB poster path to build a regular URL. + */ + override fun buildPosterUrl(searchResult: SearchResult): String? { + return ImageTools.posterUrlOrResolve( + searchResult.posterPath, + searchResult.tmdbId, + searchResult.language, + context + ) + } + +} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchTools.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchTools.kt deleted file mode 100644 index c38110f3f5..0000000000 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchTools.kt +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2023 Uwe Trottmann -// SPDX-License-Identifier: Apache-2.0 - -package com.battlelancer.seriesguide.shows.search.discover - -import android.content.Context -import com.battlelancer.seriesguide.SgApp -import com.uwetrottmann.tmdb2.entities.BaseTvShow - -object SearchTools { - - /** - * Maps TMDB TV shows to search results. - */ - fun mapTvShowsToSearchResults( - languageCode: String, - results: List - ): List { - return results.mapNotNull { tvShow -> - val tmdbId = tvShow.id ?: return@mapNotNull null - SearchResult().also { - it.tmdbId = tmdbId - it.title = tvShow.name - it.overview = tvShow.overview - it.language = languageCode - it.posterPath = tvShow.poster_path - } - } - } - - /** - * Replaces with local poster (e.g. if the user added the show in a different language to - * ensure it shows up with the same poster and to avoid fetching another image). - */ - fun markLocalShowsAsAddedAndPreferLocalPoster(context: Context, results: List?) { - if (results == null) { - return - } - - val localShowsToPoster = - SgApp.getServicesComponent(context).showTools().getTmdbIdsToPoster() - for (result in results) { - if (localShowsToPoster.indexOfKey(result.tmdbId) >= 0) { - // Is already in local database. - result.state = SearchResult.STATE_ADDED - // Use the poster already fetched for it. - val posterOrNull = localShowsToPoster[result.tmdbId] - if (posterOrNull != null) { - result.posterPath = posterOrNull - } - } - } - } - -} \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverLiveData.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverLiveData.kt index d3499eeb1a..75f583eadf 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverLiveData.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverLiveData.kt @@ -67,7 +67,7 @@ class ShowsDiscoverLiveData( } private suspend fun getShowsWithNewEpisodes( - language: String, + languageCode: String, watchProviderIds: List?, watchRegion: String?, firstReleaseYear: Int?, @@ -76,7 +76,7 @@ class ShowsDiscoverLiveData( val tmdb = SgApp.getServicesComponent(context.applicationContext).tmdb() val results = TmdbTools2().getShowsWithNewEpisodes( tmdb = tmdb, - language = language, + language = languageCode, page = 1, firstReleaseYear = firstReleaseYear, originalLanguage = originalLanguage, @@ -85,8 +85,8 @@ class ShowsDiscoverLiveData( )?.results val result = if (results != null) { - val searchResults = SearchTools.mapTvShowsToSearchResults(language, results) - SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults) + val searchResults = TmdbSearchResultMapper(context, languageCode) + .mapToSearchResults(results) buildResultSuccess(searchResults, R.string.add_empty) } else { buildResultFailure() diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddLoader.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddLoader.kt index b9f34cde2c..6c75127cba 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddLoader.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddLoader.kt @@ -5,7 +5,6 @@ package com.battlelancer.seriesguide.shows.search.discover import android.content.Context import androidx.annotation.StringRes -import androidx.collection.SparseArrayCompat import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.SgApp import com.battlelancer.seriesguide.shows.ShowsSettings @@ -97,13 +96,11 @@ class TraktAddLoader( return buildResultSuccess(emptyList()) } - return buildResultSuccess( - parseTraktShowsToSearchResults( - shows, - SgApp.getServicesComponent(context).showTools().getTmdbIdsToPoster(), - ShowsSettings.getShowsSearchLanguage(context) - ) - ) + + val searchResults = + TraktSearchResultMapper(context, ShowsSettings.getShowsSearchLanguage(context)) + .mapToSearchResults(shows) + return buildResultSuccess(searchResults) } private fun buildResultSuccess(results: List): Result { @@ -124,45 +121,4 @@ class TraktAddLoader( return Result(LinkedList(), context, errorResId) } - - /** - * Transforms a list of Trakt shows to a list of [SearchResult], marks shows already in - * the local database as added. - */ - private fun parseTraktShowsToSearchResults( - traktShows: List, - existingPosterPaths: SparseArrayCompat, - overrideLanguage: String - ): List { - val results: MutableList = ArrayList() - - // build list - for (baseShow in traktShows) { - val show = baseShow.show - val tmdbId = show?.ids?.tmdb - ?: continue // has no TMDB id - - val result = SearchResult().also { - it.tmdbId = tmdbId - it.title = show.title - // Trakt might not return an overview, so use the year if available - it.overview = if (!show.overview.isNullOrEmpty()) { - show.overview - } else if (show.year != null) { - show.year!!.toString() - } else { - "" - } - if (existingPosterPaths.indexOfKey(tmdbId) >= 0) { - // is already in local database - it.state = SearchResult.STATE_ADDED - // use the poster fetched for it (or null if there is none) - it.posterPath = existingPosterPaths[tmdbId] - } - it.language = overrideLanguage - } - results.add(result) - } - return results - } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsViewModel.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsViewModel.kt index a3663acf94..be1ee64c23 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsViewModel.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsViewModel.kt @@ -11,7 +11,7 @@ import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.SgApp import com.battlelancer.seriesguide.shows.ShowsSettings import com.battlelancer.seriesguide.shows.search.discover.SearchResult -import com.battlelancer.seriesguide.shows.search.discover.SearchTools +import com.battlelancer.seriesguide.shows.search.discover.TmdbSearchResultMapper import com.battlelancer.seriesguide.util.Errors import com.uwetrottmann.androidutils.AndroidUtils import kotlinx.coroutines.Dispatchers @@ -63,10 +63,8 @@ class SimilarShowsViewModel( page.results } - val searchResults = SearchTools.mapTvShowsToSearchResults(languageCode, results) - // Mark local shows and use existing posters. - SearchTools.markLocalShowsAsAddedAndPreferLocalPoster(context, searchResults) - + val searchResults = TmdbSearchResultMapper(context, languageCode) + .mapToSearchResults(results) postSuccessfulResult(searchResults) } } From 1ebd36e411a2eb256f72bb7042de4f9c0a908d87 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 11:37:13 +0200 Subject: [PATCH 02/13] SearchResult: do no longer implement Parcelable This was used to pass SearchResult via Intent Bundles which has been removed a while ago. --- .../shows/search/discover/SearchResult.java | 44 +------------------ 1 file changed, 2 insertions(+), 42 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java index 211901a047..55868a5eee 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java @@ -1,15 +1,12 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2018-2024 Uwe Trottmann package com.battlelancer.seriesguide.shows.search.discover; -import android.os.Parcel; -import android.os.Parcelable; - /** * Holds a search result, used later for adding this show. Supplying a poster URL is optional. */ -public class SearchResult implements Parcelable { +public class SearchResult { public static final int STATE_ADD = 0; public static final int STATE_ADDING = 1; @@ -23,30 +20,9 @@ public class SearchResult implements Parcelable { private String posterPath; private int state; - public static final Parcelable.Creator CREATOR - = new Parcelable.Creator() { - public SearchResult createFromParcel(Parcel in) { - return new SearchResult(in); - } - - public SearchResult[] newArray(int size) { - return new SearchResult[size]; - } - }; - public SearchResult() { } - public SearchResult(Parcel in) { - setTvdbid(in.readInt()); - setTmdbId(in.readInt()); - setLanguage(in.readString()); - setTitle(in.readString()); - setOverview(in.readString()); - setPosterPath(in.readString()); - setState(in.readInt()); - } - public SearchResult copy() { SearchResult copy = new SearchResult(); copy.setTvdbid(this.getTvdbid()); @@ -59,22 +35,6 @@ public SearchResult copy() { return copy; } - @Override - public int describeContents() { - return hashCode(); - } - - @Override - public void writeToParcel(Parcel dest, int flags) { - dest.writeInt(getTvdbid()); - dest.writeInt(getTmdbId()); - dest.writeString(getLanguage()); - dest.writeString(getTitle()); - dest.writeString(getOverview()); - dest.writeString(getPosterPath()); - dest.writeInt(getState()); - } - /** * @deprecated Use {@link #getTmdbId()} instead. */ From f8734e02c8c650a5f315344d55f7551bb9632fbd Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 11:38:30 +0200 Subject: [PATCH 03/13] SearchResult: remove unused TVDB ID field --- .../shows/search/discover/SearchResult.java | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java index 55868a5eee..26e83530da 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java @@ -12,7 +12,6 @@ public class SearchResult { public static final int STATE_ADDING = 1; public static final int STATE_ADDED = 2; - private int tvdbid; private int tmdbId; private String language; private String title; @@ -25,7 +24,6 @@ public SearchResult() { public SearchResult copy() { SearchResult copy = new SearchResult(); - copy.setTvdbid(this.getTvdbid()); copy.setTmdbId(this.getTmdbId()); copy.setLanguage(this.getLanguage()); copy.setTitle(this.getTitle()); @@ -35,20 +33,6 @@ public SearchResult copy() { return copy; } - /** - * @deprecated Use {@link #getTmdbId()} instead. - */ - public int getTvdbid() { - return tvdbid; - } - - /** - * @deprecated Use {@link #setTmdbId(int)} instead. - */ - public void setTvdbid(int tvdbid) { - this.tvdbid = tvdbid; - } - public int getTmdbId() { return tmdbId; } From b36a674334801afa707ee1b2853ce3105242dcf5 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 11:43:03 +0200 Subject: [PATCH 04/13] Rename .java to .kt --- .../shows/search/discover/{SearchResult.java => SearchResult.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/{SearchResult.java => SearchResult.kt} (100%) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt similarity index 100% rename from app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.java rename to app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt From ef162a06c6fd3c9cf57bcf73bd968b6e4c048018 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 11:43:03 +0200 Subject: [PATCH 05/13] Kotlin: convert SearchResult --- .../shows/search/discover/SearchResult.kt | 102 +++++------------- 1 file changed, 26 insertions(+), 76 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt index 26e83530da..e8d8dfbd0a 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt @@ -1,84 +1,34 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2018-2024 Uwe Trottmann - -package com.battlelancer.seriesguide.shows.search.discover; +package com.battlelancer.seriesguide.shows.search.discover /** * Holds a search result, used later for adding this show. Supplying a poster URL is optional. */ -public class SearchResult { - - public static final int STATE_ADD = 0; - public static final int STATE_ADDING = 1; - public static final int STATE_ADDED = 2; - - private int tmdbId; - private String language; - private String title; - private String overview; - private String posterPath; - private int state; - - public SearchResult() { - } - - public SearchResult copy() { - SearchResult copy = new SearchResult(); - copy.setTmdbId(this.getTmdbId()); - copy.setLanguage(this.getLanguage()); - copy.setTitle(this.getTitle()); - copy.setOverview(this.getOverview()); - copy.setPosterPath(this.getPosterPath()); - copy.setState(this.getState()); - return copy; - } - - public int getTmdbId() { - return tmdbId; - } - - public void setTmdbId(int tmdbId) { - this.tmdbId = tmdbId; - } - - /** Two-letter ISO 639-1 language code plus ISO-3166-1 region tag. */ - public String getLanguage() { - return language; - } - - public void setLanguage(String language) { - this.language = language; - } - - public String getTitle() { - return title; - } - - public void setTitle(String title) { - this.title = title; - } - - public String getOverview() { - return overview; - } - - public void setOverview(String overview) { - this.overview = overview; - } - - public String getPosterPath() { - return posterPath; - } - - public void setPosterPath(String posterPath) { - this.posterPath = posterPath; - } - - public int getState() { - return state; - } - - public void setState(int state) { - this.state = state; +class SearchResult { + var tmdbId: Int = 0 + + /** Two-letter ISO 639-1 language code plus ISO-3166-1 region tag. */ + var language: String? = null + var title: String = "" + var overview: String? = null + var posterPath: String? = null + var state: Int = 0 + + fun copy(): SearchResult { + val copy = SearchResult() + copy.tmdbId = tmdbId + copy.language = language + copy.title = title + copy.overview = overview + copy.posterPath = posterPath + copy.state = state + return copy + } + + companion object { + const val STATE_ADD: Int = 0 + const val STATE_ADDING: Int = 1 + const val STATE_ADDED: Int = 2 } } From eff408b244caa11ca08032660d74c6d309ba648b Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 12:26:04 +0200 Subject: [PATCH 06/13] Rename .java to .kt --- .../seriesguide/shows/tools/{AddShowTask.java => AddShowTask.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/battlelancer/seriesguide/shows/tools/{AddShowTask.java => AddShowTask.kt} (100%) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.java b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt similarity index 100% rename from app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.java rename to app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt From 1c72b7f1f66c9c8e039e835fe34e9eaf56baf8d8 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 12:26:04 +0200 Subject: [PATCH 07/13] Kotlin: convert AddShowTask --- .../seriesguide/shows/tools/AddShowTask.kt | 511 +++++++++--------- 1 file changed, 254 insertions(+), 257 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt index d176f6b51e..cfc31cee57 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt @@ -1,361 +1,358 @@ // Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 -package com.battlelancer.seriesguide.shows.tools; - -import android.annotation.SuppressLint; -import android.content.Context; -import android.os.AsyncTask; -import android.widget.Toast; -import androidx.annotation.Nullable; -import androidx.preference.PreferenceManager; -import com.battlelancer.seriesguide.R; -import com.battlelancer.seriesguide.SgApp; -import com.battlelancer.seriesguide.backend.settings.HexagonSettings; -import com.battlelancer.seriesguide.modules.ServicesComponent; -import com.battlelancer.seriesguide.provider.SeriesGuideDatabase; -import com.battlelancer.seriesguide.shows.search.discover.SearchResult; -import com.battlelancer.seriesguide.shows.tools.AddUpdateShowTools.ShowResult; -import com.battlelancer.seriesguide.sync.HexagonEpisodeSync; -import com.battlelancer.seriesguide.traktapi.TraktCredentials; -import com.battlelancer.seriesguide.traktapi.TraktSettings; -import com.battlelancer.seriesguide.traktapi.TraktTools2; -import com.battlelancer.seriesguide.util.Errors; -import com.battlelancer.seriesguide.util.TaskManager; -import com.uwetrottmann.androidutils.AndroidUtils; -import com.uwetrottmann.trakt5.entities.BaseShow; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import kotlin.Pair; -import org.greenrobot.eventbus.EventBus; -import timber.log.Timber; +package com.battlelancer.seriesguide.shows.tools + +import android.annotation.SuppressLint +import android.content.Context +import android.os.AsyncTask +import android.widget.Toast +import androidx.preference.PreferenceManager +import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.SgApp +import com.battlelancer.seriesguide.backend.settings.HexagonSettings +import com.battlelancer.seriesguide.backend.settings.HexagonSettings.isEnabled +import com.battlelancer.seriesguide.provider.SeriesGuideDatabase +import com.battlelancer.seriesguide.shows.search.discover.SearchResult +import com.battlelancer.seriesguide.shows.tools.AddUpdateShowTools.ShowResult +import com.battlelancer.seriesguide.sync.HexagonEpisodeSync +import com.battlelancer.seriesguide.traktapi.TraktCredentials.Companion.get +import com.battlelancer.seriesguide.traktapi.TraktSettings +import com.battlelancer.seriesguide.traktapi.TraktTools2 +import com.battlelancer.seriesguide.traktapi.TraktTools2.ServiceResult +import com.battlelancer.seriesguide.util.Errors +import com.battlelancer.seriesguide.util.TaskManager +import com.uwetrottmann.androidutils.AndroidUtils +import com.uwetrottmann.trakt5.entities.BaseShow +import org.greenrobot.eventbus.EventBus +import timber.log.Timber +import java.util.LinkedList /** * Adds shows to the local database, tries to get watched and collected episodes if a trakt account * is connected. */ -public class AddShowTask extends AsyncTask { - - public static class OnShowAddedEvent { - - public final boolean successful; +class AddShowTask( + context: Context, + shows: List, + isSilentMode: Boolean, + isMergingShows: Boolean +) : AsyncTask() { + + class OnShowAddedEvent private constructor( /** * Is -1 if add task was aborted. */ - public final int showTmdbId; - private final String message; - - private OnShowAddedEvent(int showTmdbId, String message, boolean successful) { - this.showTmdbId = showTmdbId; - this.message = message; - this.successful = successful; - } - - public void handle(Context context) { + val showTmdbId: Int, + private val message: String?, + val successful: Boolean + ) { + fun handle(context: Context?) { if (message != null) { - Toast.makeText(context, message, Toast.LENGTH_LONG).show(); + Toast.makeText(context, message, Toast.LENGTH_LONG).show() } } - public static OnShowAddedEvent successful(int showTmdbId) { - return new OnShowAddedEvent(showTmdbId, null, true); - } + companion object { + fun successful(showTmdbId: Int): OnShowAddedEvent { + return OnShowAddedEvent(showTmdbId, null, true) + } - public static OnShowAddedEvent exists(Context context, int showTmdbId, String showTitle) { - return new OnShowAddedEvent(showTmdbId, - context.getString(R.string.add_already_exists, showTitle), true); - } + fun exists(context: Context, showTmdbId: Int, showTitle: String?): OnShowAddedEvent { + return OnShowAddedEvent( + showTmdbId, + context.getString(R.string.add_already_exists, showTitle), + true + ) + } - public static OnShowAddedEvent failed(Context context, int showTmdbId, String showTitle) { - return new OnShowAddedEvent(showTmdbId, + fun failed(context: Context, showTmdbId: Int, showTitle: String?): OnShowAddedEvent { + return OnShowAddedEvent( + showTmdbId, context.getString(R.string.add_error, showTitle), - false); - } + false + ) + } - public static OnShowAddedEvent failedDetails(Context context, int showTmdbId, - String showTitle, String details) { - return new OnShowAddedEvent(showTmdbId, - String.format("%s %s", context.getString(R.string.add_error, showTitle), - details), - false); - } + fun failedDetails( + context: Context, + showTmdbId: Int, + showTitle: String?, + details: String? + ): OnShowAddedEvent { + return OnShowAddedEvent( + showTmdbId, + String.format( + "%s %s", context.getString(R.string.add_error, showTitle), + details + ), + false + ) + } - public static OnShowAddedEvent aborted(String message) { - return new OnShowAddedEvent(-1, message, false); + fun aborted(message: String): OnShowAddedEvent { + return OnShowAddedEvent(-1, message, false) + } } } - private static final int PROGRESS_EXISTS = 0; - private static final int PROGRESS_SUCCESS = 1; - private static final int PROGRESS_ERROR = 2; - private static final int PROGRESS_ERROR_TMDB = 3; - private static final int PROGRESS_ERROR_DOES_NOT_EXIST = 4; - private static final int PROGRESS_ERROR_HEXAGON = 6; - private static final int PROGRESS_ERROR_DATA = 7; - private static final int RESULT_OFFLINE = 8; - private static final int RESULT_TRAKT_API_ERROR = 9; - private static final int RESULT_TRAKT_AUTH_ERROR = 10; - - @SuppressLint("StaticFieldLeak") private final Context context; - private final LinkedList addQueue = new LinkedList<>(); - - private boolean isFinishedAddingShows = false; - private boolean isSilentMode; - private boolean isMergingShows; - - public AddShowTask(Context context, List shows, boolean isSilentMode, - boolean isMergingShows) { - this.context = context.getApplicationContext(); - this.addQueue.addAll(shows); - this.isSilentMode = isSilentMode; - this.isMergingShows = isMergingShows; + @SuppressLint("StaticFieldLeak") + private val context: Context = context.applicationContext + private val addQueue = LinkedList() + + private var isFinishedAddingShows = false + private var isSilentMode: Boolean + private var isMergingShows: Boolean + + init { + addQueue.addAll(shows) + this.isSilentMode = isSilentMode + this.isMergingShows = isMergingShows } /** * Adds shows to the add queue. If this returns false, the shows were not added because the task * is finishing up. Create a new one instead. */ - public boolean addShows(List show, boolean isSilentMode, boolean isMergingShows) { + fun addShows( + show: List, + isSilentMode: Boolean, + isMergingShows: Boolean + ): Boolean { if (isFinishedAddingShows) { - Timber.d("addShows: failed, already finishing up."); - return false; + Timber.d("addShows: failed, already finishing up.") + return false } else { - this.isSilentMode = isSilentMode; + this.isSilentMode = isSilentMode // never reset isMergingShows once true, so merged flag is correctly set on completion - this.isMergingShows = this.isMergingShows || isMergingShows; - addQueue.addAll(show); - Timber.d("addShows: added shows to queue."); - return true; + this.isMergingShows = this.isMergingShows || isMergingShows + addQueue.addAll(show) + Timber.d("addShows: added shows to queue.") + return true } } - @Override - protected Void doInBackground(Void... params) { - Timber.d("Starting to add shows..."); + @Deprecated("Deprecated in Java") + override fun doInBackground(vararg params: Void?): Void? { + Timber.d("Starting to add shows...") - SearchResult firstShow = addQueue.peek(); + val firstShow = addQueue.peek() if (firstShow == null) { - Timber.d("Finished. Queue was empty."); - return null; + Timber.d("Finished. Queue was empty.") + return null } if (!AndroidUtils.isNetworkConnected(context)) { - Timber.d("Finished. No internet connection."); - publishProgress(RESULT_OFFLINE, firstShow.getTmdbId(), firstShow.getTitle()); - return null; + Timber.d("Finished. No internet connection.") + publishProgress(RESULT_OFFLINE, firstShow.tmdbId, firstShow.title) + return null } - if (isCancelled()) { - Timber.d("Finished. Cancelled."); - return null; + if (isCancelled) { + Timber.d("Finished. Cancelled.") + return null } // if not connected to Hexagon, get episodes from trakt - Map traktCollection = null; - Map traktWatched = null; - if (!HexagonSettings.isEnabled(context) && TraktCredentials.get(context).hasCredentials()) { - Timber.d("Getting watched and collected episodes from trakt."); + var traktCollection: Map? = null + var traktWatched: Map? = null + if (!isEnabled(context) && get(context).hasCredentials()) { + Timber.d("Getting watched and collected episodes from trakt.") // get collection - Map traktShows = getTraktShows(true); - if (traktShows == null) { - return null; // can not get collected state from trakt, give up. - } - traktCollection = traktShows; + traktCollection = getTraktShows(true) + ?: return null // can not get collected state from trakt, give up. // get watched - traktShows = getTraktShows(false); - if (traktShows == null) { - return null; // can not get watched state from trakt, give up. - } - traktWatched = traktShows; + traktWatched = getTraktShows(false) + ?: return null // can not get watched state from trakt, give up. } - ServicesComponent services = SgApp.getServicesComponent(context); - HexagonEpisodeSync hexagonEpisodeSync = new HexagonEpisodeSync(context, - services.hexagonTools()); - AddUpdateShowTools showTools = services.addUpdateShowTools(); + val services = SgApp.getServicesComponent(context) + val hexagonEpisodeSync = HexagonEpisodeSync(context, services.hexagonTools()) + val showTools = services.addUpdateShowTools() - int result; - boolean addedAtLeastOneShow = false; - boolean failedMergingShows = false; + var result: Int + var addedAtLeastOneShow = false + var failedMergingShows = false while (!addQueue.isEmpty()) { - Timber.d("Starting to add next show..."); - if (isCancelled()) { - Timber.d("Finished. Cancelled."); + Timber.d("Starting to add next show...") + if (isCancelled) { + Timber.d("Finished. Cancelled.") // only cancelled on config change, so don't rebuild fts // table yet - return null; + return null } - SearchResult nextShow = addQueue.removeFirst(); + val nextShow = addQueue.removeFirst() // set values required for progress update - String currentShowName = nextShow.getTitle(); - int currentShowTmdbId = nextShow.getTmdbId(); + val currentShowName = nextShow.title + val currentShowTmdbId = nextShow.tmdbId if (currentShowTmdbId <= 0) { // Invalid ID, should never have been passed, report. // Background: Hexagon gets requests with ID 0. - IllegalStateException invalidIdException = new IllegalStateException( - "Show id invalid: " + currentShowTmdbId - + ", silentMode=" + isSilentMode - + ", merging=" + isMergingShows - ); - Errors.logAndReport("Add show", invalidIdException); - continue; + val invalidIdException = + IllegalStateException("Show id invalid: $currentShowTmdbId, silentMode=$isSilentMode, merging=$isMergingShows") + Errors.logAndReport("Add show", invalidIdException) + continue } if (!AndroidUtils.isNetworkConnected(context)) { - Timber.d("Finished. No connection."); - publishProgress(RESULT_OFFLINE, currentShowTmdbId, currentShowName); - failedMergingShows = true; - break; + Timber.d("Finished. No connection.") + publishProgress(RESULT_OFFLINE, currentShowTmdbId, currentShowName) + failedMergingShows = true + break } - ShowResult addResult = showTools.addShow(nextShow.getTmdbId(), nextShow.getLanguage(), - traktCollection, traktWatched, hexagonEpisodeSync); - if (addResult == ShowResult.SUCCESS) { - result = PROGRESS_SUCCESS; - addedAtLeastOneShow = true; - } else if (addResult == ShowResult.IN_DATABASE) { - result = PROGRESS_EXISTS; - } else { - Timber.e("Adding show failed: %s", addResult); - - // Only fail a hexagon merge if show can not be added due to network error, - // not because it does not (longer) exist. - if (isMergingShows && addResult != ShowResult.DOES_NOT_EXIST) { - failedMergingShows = true; + val addResult = showTools.addShow( + nextShow.tmdbId, nextShow.language, + traktCollection, traktWatched, hexagonEpisodeSync + ) + when (addResult) { + ShowResult.SUCCESS -> { + result = PROGRESS_SUCCESS + addedAtLeastOneShow = true + } + + ShowResult.IN_DATABASE -> { + result = PROGRESS_EXISTS } - switch (addResult) { - case DOES_NOT_EXIST: - result = PROGRESS_ERROR_DOES_NOT_EXIST; - break; - case TMDB_ERROR: - result = PROGRESS_ERROR_TMDB; - break; - case HEXAGON_ERROR: - result = PROGRESS_ERROR_HEXAGON; - break; - case DATABASE_ERROR: - result = PROGRESS_ERROR_DATA; - break; - default: - result = PROGRESS_ERROR; - break; + else -> { + Timber.e("Adding show failed: %s", addResult) + + // Only fail a hexagon merge if show can not be added due to network error, + // not because it does not (longer) exist. + if (isMergingShows && addResult != ShowResult.DOES_NOT_EXIST) { + failedMergingShows = true + } + + result = when (addResult) { + ShowResult.DOES_NOT_EXIST -> PROGRESS_ERROR_DOES_NOT_EXIST + ShowResult.TMDB_ERROR -> PROGRESS_ERROR_TMDB + ShowResult.HEXAGON_ERROR -> PROGRESS_ERROR_HEXAGON + ShowResult.DATABASE_ERROR -> PROGRESS_ERROR_DATA + else -> PROGRESS_ERROR + } } } - publishProgress(result, currentShowTmdbId, currentShowName); - Timber.d("Finished adding show. (Result code: %s)", result); + publishProgress(result, currentShowTmdbId, currentShowName) + Timber.d("Finished adding show. (Result code: %s)", result) } - isFinishedAddingShows = true; + isFinishedAddingShows = true // when merging shows down from Hexagon, set success flag if (isMergingShows && !failedMergingShows) { - HexagonSettings.setHasMergedShows(context, true); + HexagonSettings.setHasMergedShows(context, true) } if (addedAtLeastOneShow) { // make sure the next sync will download all ratings PreferenceManager.getDefaultSharedPreferences(context).edit() - .putLong(TraktSettings.KEY_LAST_SHOWS_RATED_AT, 0) - .putLong(TraktSettings.KEY_LAST_EPISODES_RATED_AT, 0) - .apply(); + .putLong(TraktSettings.KEY_LAST_SHOWS_RATED_AT, 0) + .putLong(TraktSettings.KEY_LAST_EPISODES_RATED_AT, 0) + .apply() // renew FTS3 table - Timber.d("Renewing search table."); - SeriesGuideDatabase.rebuildFtsTable(context); + Timber.d("Renewing search table.") + SeriesGuideDatabase.rebuildFtsTable(context) } - Timber.d("Finished adding shows."); - return null; + Timber.d("Finished adding shows.") + return null } - @Override - protected void onProgressUpdate(String... values) { + @Deprecated("Deprecated in Java") + override fun onProgressUpdate(vararg values: String) { if (isSilentMode) { - Timber.d("SILENT MODE: do not show progress toast"); - return; + Timber.d("SILENT MODE: do not show progress toast") + return } - // passing tvdb id and show name through values as fields may already have been - // overwritten on processing thread - - OnShowAddedEvent event = null; - // not catching format/null exceptions, if they happen we made a mistake passing values - int result = Integer.parseInt(values[0]); - int showTmdbId = Integer.parseInt(values[1]); - String showTitle = values[2]; - switch (result) { - case PROGRESS_SUCCESS: + // Not catching format/null exceptions, should not occur if values correctly passed. + val result = values[0].toInt() + val showTmdbId = values[1].toInt() + val showTitle = values[2] + val event = when (result) { + PROGRESS_SUCCESS -> // do nothing, user will see show added to show list - event = OnShowAddedEvent.successful(showTmdbId); - break; - case PROGRESS_EXISTS: - event = OnShowAddedEvent.exists(context, showTmdbId, showTitle); - break; - case PROGRESS_ERROR: - event = OnShowAddedEvent.failed(context, showTmdbId, showTitle); - break; - case PROGRESS_ERROR_TMDB: - event = OnShowAddedEvent.failedDetails(context, showTmdbId, showTitle, - context.getString(R.string.api_error_generic, - context.getString(R.string.tmdb))); - break; - case PROGRESS_ERROR_DOES_NOT_EXIST: - event = OnShowAddedEvent.failedDetails(context, showTmdbId, showTitle, - context.getString(R.string.tvdb_error_does_not_exist)); - break; - case PROGRESS_ERROR_HEXAGON: - event = OnShowAddedEvent.failedDetails(context, showTmdbId, showTitle, - context.getString(R.string.api_error_generic, - context.getString(R.string.hexagon))); - break; - case PROGRESS_ERROR_DATA: - event = OnShowAddedEvent.failedDetails(context, showTmdbId, showTitle, - context.getString(R.string.database_error)); - break; - case RESULT_OFFLINE: - event = OnShowAddedEvent.aborted(context.getString(R.string.offline)); - break; - case RESULT_TRAKT_API_ERROR: - event = OnShowAddedEvent.aborted(context.getString(R.string.api_error_generic, - context.getString(R.string.trakt))); - break; - case RESULT_TRAKT_AUTH_ERROR: - event = OnShowAddedEvent - .aborted(context.getString(R.string.trakt_error_credentials)); - break; + OnShowAddedEvent.successful(showTmdbId) + + PROGRESS_EXISTS -> OnShowAddedEvent.exists(context, showTmdbId, showTitle) + + PROGRESS_ERROR -> OnShowAddedEvent.failed(context, showTmdbId, showTitle) + + PROGRESS_ERROR_TMDB -> OnShowAddedEvent.failedDetails( + context, showTmdbId, showTitle, + context.getString(R.string.api_error_generic, context.getString(R.string.tmdb)) + ) + + PROGRESS_ERROR_DOES_NOT_EXIST -> OnShowAddedEvent.failedDetails( + context, showTmdbId, showTitle, + context.getString(R.string.tvdb_error_does_not_exist) + ) + + PROGRESS_ERROR_HEXAGON -> OnShowAddedEvent.failedDetails( + context, showTmdbId, showTitle, + context.getString(R.string.api_error_generic, context.getString(R.string.hexagon)) + ) + + PROGRESS_ERROR_DATA -> OnShowAddedEvent.failedDetails( + context, showTmdbId, showTitle, context.getString(R.string.database_error) + ) + + RESULT_OFFLINE -> OnShowAddedEvent.aborted(context.getString(R.string.offline)) + + RESULT_TRAKT_API_ERROR -> OnShowAddedEvent.aborted( + context.getString(R.string.api_error_generic, context.getString(R.string.trakt)) + ) + + RESULT_TRAKT_AUTH_ERROR -> OnShowAddedEvent.aborted( + context.getString(R.string.trakt_error_credentials) + ) + + else -> null } if (event != null) { - EventBus.getDefault().post(event); + EventBus.getDefault().post(event) } } - @Override - protected void onPostExecute(Void aVoid) { - TaskManager.getInstance().releaseAddTaskRef(); + @Deprecated("Deprecated in Java") + override fun onPostExecute(aVoid: Void?) { + TaskManager.getInstance().releaseAddTaskRef() } - private void publishProgress(int result) { - publishProgress(String.valueOf(result), "0", ""); + private fun publishProgress(result: Int) { + publishProgress(result.toString(), "0", "") } - private void publishProgress(int result, int showTmdbId, String showTitle) { - publishProgress(String.valueOf(result), String.valueOf(showTmdbId), showTitle); + private fun publishProgress(result: Int, showTmdbId: Int, showTitle: String) { + publishProgress(result.toString(), showTmdbId.toString(), showTitle) } - @Nullable - private Map getTraktShows(boolean isCollectionNotWatched) { - Pair, TraktTools2.ServiceResult> result = TraktTools2 - .getCollectedOrWatchedShows(isCollectionNotWatched, context); - if (result.getSecond() == TraktTools2.ServiceResult.AUTH_ERROR) { - publishProgress(RESULT_TRAKT_AUTH_ERROR); - } else if (result.getSecond() == TraktTools2.ServiceResult.API_ERROR) { - publishProgress(RESULT_TRAKT_API_ERROR); + private fun getTraktShows(isCollectionNotWatched: Boolean): Map? { + val result: Pair?, ServiceResult> = + TraktTools2.getCollectedOrWatchedShows(isCollectionNotWatched, context) + if (result.second == ServiceResult.AUTH_ERROR) { + publishProgress(RESULT_TRAKT_AUTH_ERROR) + } else if (result.second == ServiceResult.API_ERROR) { + publishProgress(RESULT_TRAKT_API_ERROR) } - return result.getFirst(); + return result.first + } + + companion object { + private const val PROGRESS_EXISTS = 0 + private const val PROGRESS_SUCCESS = 1 + private const val PROGRESS_ERROR = 2 + private const val PROGRESS_ERROR_TMDB = 3 + private const val PROGRESS_ERROR_DOES_NOT_EXIST = 4 + private const val PROGRESS_ERROR_HEXAGON = 6 + private const val PROGRESS_ERROR_DATA = 7 + private const val RESULT_OFFLINE = 8 + private const val RESULT_TRAKT_API_ERROR = 9 + private const val RESULT_TRAKT_AUTH_ERROR = 10 } } From 07c8004774ca7a2cfcd4cb23a73eed84e40d7655 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 12:58:37 +0200 Subject: [PATCH 08/13] Rename .java to .kt --- .../seriesguide/util/{TaskManager.java => TaskManager.kt} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename app/src/main/java/com/battlelancer/seriesguide/util/{TaskManager.java => TaskManager.kt} (100%) diff --git a/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.java b/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt similarity index 100% rename from app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.java rename to app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt From 715c7d96de8da6f45f5895dcb8d950b36d0b014a Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 12:58:37 +0200 Subject: [PATCH 09/13] Kotlin: convert TaskManager --- .../dataliberation/AutoBackupFragment.kt | 4 +- .../dataliberation/JsonExportTask.kt | 2 +- .../dataliberation/JsonImportTask.kt | 5 +- .../seriesguide/history/HistoryActivity.kt | 2 +- .../seriesguide/shows/FirstRunView.kt | 4 +- .../seriesguide/shows/ShowsActivityImpl.kt | 4 +- .../shows/ShowsDistillationFragment.kt | 2 +- .../shows/search/SearchActivityImpl.kt | 2 +- .../shows/search/discover/AddShowPopupMenu.kt | 2 +- .../discover/ItemAddShowClickListener.kt | 2 +- .../discover/ShowsDiscoverPagingActivity.kt | 2 +- .../search/discover/ShowsTraktActivity.kt | 2 +- .../shows/search/discover/TraktAddFragment.kt | 6 +- .../search/similar/SimilarShowsActivity.kt | 4 +- .../seriesguide/shows/tools/AddShowTask.kt | 4 +- .../shows/tools/LatestEpisodeUpdateTask.java | 4 +- .../seriesguide/sync/HexagonSync.java | 4 +- .../seriesguide/sync/SgSyncAdapter.kt | 2 +- .../seriesguide/ui/BaseActivity.kt | 4 +- .../seriesguide/util/TaskManager.kt | 145 +++++++++--------- 20 files changed, 104 insertions(+), 102 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/AutoBackupFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/AutoBackupFragment.kt index 4027462fe3..d9e554a988 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/AutoBackupFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/AutoBackupFragment.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2013-2024 Uwe Trottmann package com.battlelancer.seriesguide.dataliberation @@ -63,7 +63,7 @@ class AutoBackupFragment : Fragment() { } binding.buttonAutoBackupNow.setOnClickListener { - if (TaskManager.getInstance().tryBackupTask(requireContext())) { + if (TaskManager.tryBackupTask(requireContext())) { setProgressLock(true) } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonExportTask.kt b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonExportTask.kt index a9fcdf83a9..650bc4036c 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonExportTask.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonExportTask.kt @@ -174,7 +174,7 @@ class JsonExportTask( } private fun onPostExecute(result: Int) { - TaskManager.getInstance().releaseBackupTaskRef() + TaskManager.releaseBackupTaskRef() if (!isAutoBackupMode) { val messageId: Int diff --git a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonImportTask.kt b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonImportTask.kt index 72b79335f0..a62e5959be 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonImportTask.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/dataliberation/JsonImportTask.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2013-2024 Uwe Trottmann package com.battlelancer.seriesguide.dataliberation @@ -120,8 +120,7 @@ class JsonImportTask( private fun doInBackground(coroutineScope: CoroutineScope): Int { // Ensure no large database ops are running - val tm = TaskManager.getInstance() - if (SgSyncAdapter.isSyncActive(context, false) || tm.isAddTaskRunning) { + if (SgSyncAdapter.isSyncActive(context, false) || TaskManager.isAddTaskRunning) { return ERROR_LARGE_DB_OP } diff --git a/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt index f79d8a9e5c..44304dd30a 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt @@ -64,7 +64,7 @@ class HistoryActivity : BaseActivity(), OnAddShowListener { * Called if the user adds a show from a trakt stream fragment. */ override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } companion object { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/FirstRunView.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/FirstRunView.kt index 14cff4578c..3d2e205603 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/FirstRunView.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/FirstRunView.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2018-2024 Uwe Trottmann package com.battlelancer.seriesguide.shows @@ -63,7 +63,7 @@ class FirstRunView @JvmOverloads constructor(context: Context, attrs: AttributeS putBoolean(DisplaySettings.KEY_PREVENT_SPOILERS, noSpoilers) } // update next episode strings right away - TaskManager.getInstance().tryNextEpisodeUpdateTask(v.context) + TaskManager.tryNextEpisodeUpdateTask(v.context) // show binding.checkboxNoSpoilers.isChecked = noSpoilers } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt index 43f3488f51..27af1af7da 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt @@ -312,7 +312,7 @@ open class ShowsActivityImpl : BaseTopActivity(), AddShowDialogFragment.OnAddSho } // update next episodes - TaskManager.getInstance().tryNextEpisodeUpdateTask(this) + TaskManager.tryNextEpisodeUpdateTask(this) } override fun onPause() { @@ -336,7 +336,7 @@ open class ShowsActivityImpl : BaseTopActivity(), AddShowDialogFragment.OnAddSho * Called if the user adds a show from a trakt stream fragment. */ override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } override val snackbarParentView: View diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsDistillationFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsDistillationFragment.kt index 8655309747..771deb5760 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsDistillationFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsDistillationFragment.kt @@ -147,7 +147,7 @@ class ShowsDistillationFragment : AppCompatDialogFragment() { override fun onNoReleasedChanged(value: Boolean) { DisplaySettings.setNoReleasedEpisodes(requireContext(), value) - TaskManager.getInstance().tryNextEpisodeUpdateTask(requireContext()) + TaskManager.tryNextEpisodeUpdateTask(requireContext()) } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt index 8c950b6e32..97d66f3398 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt @@ -194,7 +194,7 @@ open class SearchActivityImpl : BaseMessageActivity(), AddShowDialogFragment.OnA } override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt index edcf744c65..d227cc2cdc 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt @@ -39,7 +39,7 @@ class AddShowPopupMenu( R.id.menu_action_add_show_add -> { // post so other fragments can display a progress indicator for that show EventBus.getDefault().post(OnAddingShowEvent(show.tmdbId)) - TaskManager.getInstance().performAddTask(context, show) + TaskManager.performAddTask(context, show) true } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt index f778500737..a06473950f 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt @@ -45,7 +45,7 @@ open class ItemAddShowClickListener( override fun onAddClick(item: SearchResult) { EventBus.getDefault().post(OnAddingShowEvent(item.tmdbId)) - TaskManager.getInstance().performAddTask(context, item) + TaskManager.performAddTask(context, item) } override fun onMoreOptionsClick(view: View, show: SearchResult) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt index 183b4c244e..8b3c0ebc6f 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt @@ -74,7 +74,7 @@ class ShowsDiscoverPagingActivity : BaseMessageActivity(), AddShowDialogFragment } override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } companion object { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt index 3b0231c202..43689b48d9 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt @@ -62,7 +62,7 @@ class ShowsTraktActivity : BaseMessageActivity(), AddShowDialogFragment.OnAddSho } override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } companion object { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt index 39f4fd09c8..9b4308dfe4 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt @@ -178,8 +178,10 @@ class TraktAddFragment : Fragment() { } } EventBus.getDefault().post(OnAddingShowEvent()) - TaskManager.getInstance() - .performAddTask(context, showsToAdd, false, false) + TaskManager.performAddTask(requireContext(), showsToAdd, + isSilentMode = false, + isMergingShows = false + ) } // disable the item so the user has to come back menuItem.isEnabled = false diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt index 5ca52fb936..57536eaefd 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2019-2024 Uwe Trottmann package com.battlelancer.seriesguide.shows.search.similar @@ -30,7 +30,7 @@ class SimilarShowsActivity : BaseSimilarActivity(), AddShowDialogFragment.OnAddS } override fun onAddShow(show: SearchResult) { - TaskManager.getInstance().performAddTask(this, show) + TaskManager.performAddTask(this, show) } companion object { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt index cfc31cee57..7b84b72f13 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2011-2024 Uwe Trottmann package com.battlelancer.seriesguide.shows.tools @@ -321,7 +321,7 @@ class AddShowTask( @Deprecated("Deprecated in Java") override fun onPostExecute(aVoid: Void?) { - TaskManager.getInstance().releaseAddTaskRef() + TaskManager.releaseAddTaskRef() } private fun publishProgress(result: Int) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/LatestEpisodeUpdateTask.java b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/LatestEpisodeUpdateTask.java index e14980aeff..83062deb8b 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/LatestEpisodeUpdateTask.java +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/LatestEpisodeUpdateTask.java @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2014-2024 Uwe Trottmann package com.battlelancer.seriesguide.shows.tools; @@ -32,7 +32,7 @@ protected Void doInBackground(Integer... params) { @Override protected void onPostExecute(Void aVoid) { - TaskManager.getInstance().releaseNextEpisodeUpdateTaskRef(); + TaskManager.releaseNextEpisodeUpdateTaskRef(); } public static void updateLatestEpisodeFor(Context context, Long showId) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java index cf7a89404e..629ee061db 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java +++ b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2017-2024 Uwe Trottmann package com.battlelancer.seriesguide.sync; @@ -156,7 +156,7 @@ private HexagonResult syncShows(Map tmdbIdsToShowIds) { boolean addNewShows = !newShows.isEmpty(); if (addNewShows) { List newShowsList = new LinkedList<>(newShows.values()); - TaskManager.getInstance().performAddTask(context, newShowsList, true, !hasMergedShows); + TaskManager.performAddTask(context, newShowsList, true, !hasMergedShows); } else if (!hasMergedShows) { // set shows as merged HexagonSettings.setHasMergedShows(context, true); diff --git a/app/src/main/java/com/battlelancer/seriesguide/sync/SgSyncAdapter.kt b/app/src/main/java/com/battlelancer/seriesguide/sync/SgSyncAdapter.kt index 683c1930e1..c5641008ac 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/sync/SgSyncAdapter.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/sync/SgSyncAdapter.kt @@ -204,7 +204,7 @@ class SgSyncAdapter(context: Context) : AbstractThreadedSyncAdapter(context, tru if (Thread.interrupted()) throw InterruptedException() // update next episodes for all shows - TaskManager.getInstance().tryNextEpisodeUpdateTask(context) + TaskManager.tryNextEpisodeUpdateTask(context) updateTimeAndFailedCounter(prefs, resultCode) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseActivity.kt index f5bbbd7e77..705db1e827 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseActivity.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2011-2024 Uwe Trottmann package com.battlelancer.seriesguide.ui @@ -93,7 +93,7 @@ abstract class BaseActivity : BaseThemeActivity() { if (!BackupSettings.isTimeForAutoBackup(this)) { return false } - TaskManager.getInstance().tryBackupTask(this) + TaskManager.tryBackupTask(this) return true } diff --git a/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt b/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt index c2d1a5dc62..c71035ba04 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt @@ -1,49 +1,34 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2023 Uwe Trottmann -package com.battlelancer.seriesguide.util; +package com.battlelancer.seriesguide.util -import android.content.Context; -import android.os.AsyncTask; -import android.widget.Toast; -import androidx.annotation.MainThread; -import androidx.annotation.Nullable; -import com.battlelancer.seriesguide.R; -import com.battlelancer.seriesguide.dataliberation.JsonExportTask; -import com.battlelancer.seriesguide.shows.tools.AddShowTask; -import com.battlelancer.seriesguide.shows.search.discover.SearchResult; -import com.battlelancer.seriesguide.shows.tools.LatestEpisodeUpdateTask; -import java.util.ArrayList; -import java.util.List; -import kotlinx.coroutines.Job; +import android.annotation.SuppressLint +import android.content.Context +import android.os.AsyncTask +import android.widget.Toast +import androidx.annotation.MainThread +import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.dataliberation.JsonExportTask +import com.battlelancer.seriesguide.shows.search.discover.SearchResult +import com.battlelancer.seriesguide.shows.tools.AddShowTask +import com.battlelancer.seriesguide.shows.tools.LatestEpisodeUpdateTask +import kotlinx.coroutines.Job /** - * Hold some {@link AsyncTask} instances while running to ensure only one is executing at a time. + * Holds on to task instances while they are running to ensure only one is executing at a time. */ -public class TaskManager { - - private static TaskManager _instance; +object TaskManager { - @Nullable private AddShowTask addShowTask; - @Nullable private Job backupTask; - @Nullable private LatestEpisodeUpdateTask nextEpisodeUpdateTask; - - private TaskManager() { - } - - public static synchronized TaskManager getInstance() { - if (_instance == null) { - _instance = new TaskManager(); - } - return _instance; - } + @SuppressLint("StaticFieldLeak") // AddShowTask holds an application context + private var addShowTask: AddShowTask? = null + private var backupTask: Job? = null + private var nextEpisodeUpdateTask: LatestEpisodeUpdateTask? = null @MainThread - public synchronized void performAddTask(Context context, SearchResult show) { - List wrapper = new ArrayList<>(); - wrapper.add(show); - performAddTask(context, wrapper, false, false); - } + @Synchronized + fun performAddTask(context: Context, show: SearchResult) = + performAddTask(context, listOf(show), isSilentMode = false, isMergingShows = false) /** * Schedule shows to be added to the database. @@ -51,72 +36,88 @@ public class TaskManager { * @param isSilentMode Whether to display status toasts if a show could not be added. * @param isMergingShows Whether to set the Hexagon show merged flag to true if all shows were */ + @JvmStatic @MainThread - public synchronized void performAddTask(final Context context, final List shows, - final boolean isSilentMode, final boolean isMergingShows) { + @Synchronized + fun performAddTask( + context: Context, + shows: List, + isSilentMode: Boolean, + isMergingShows: Boolean + ) { if (!isSilentMode) { // notify user here already - if (shows.size() == 1) { + if (shows.size == 1) { // say title of show - SearchResult show = shows.get(0); - Toast.makeText(context, context.getString(R.string.add_started, show.getTitle()), - Toast.LENGTH_SHORT).show(); + val show = shows[0] + Toast.makeText( + context, context.getString(R.string.add_started, show.title), + Toast.LENGTH_SHORT + ).show() } else { // generic adding multiple message - Toast.makeText(context, R.string.add_multiple, Toast.LENGTH_SHORT).show(); + Toast.makeText(context, R.string.add_multiple, Toast.LENGTH_SHORT).show() } } // add the show(s) to a running add task or create a new one - //noinspection ConstantConditions: null check in isAddTaskRunning - if (!isAddTaskRunning() || !addShowTask.addShows(shows, isSilentMode, isMergingShows)) { - addShowTask = new AddShowTask(context, shows, isSilentMode, isMergingShows); - addShowTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + if (!isAddTaskRunning || !addShowTask!!.addShows(shows, isSilentMode, isMergingShows)) { + AddShowTask(context, shows, isSilentMode, isMergingShows) + .also { this.addShowTask = it } + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) } } - public synchronized void releaseAddTaskRef() { - addShowTask = null; // clear reference to avoid holding on to task context + @Synchronized + fun releaseAddTaskRef() { + addShowTask = null // clear reference to avoid holding on to task context } - public boolean isAddTaskRunning() { - return !(addShowTask == null || addShowTask.getStatus() == AsyncTask.Status.FINISHED); - } + val isAddTaskRunning: Boolean + get() = !(addShowTask == null || addShowTask!!.status == AsyncTask.Status.FINISHED) /** - * If no {@link AddShowTask} or {@link JsonExportTask} created by this {@link - * com.battlelancer.seriesguide.util.TaskManager} is running a - * {@link JsonExportTask} is scheduled in silent mode. + * If no [AddShowTask] or [JsonExportTask] created by this [TaskManager] is running a + * [JsonExportTask] is scheduled in silent mode. */ @MainThread - public synchronized boolean tryBackupTask(Context context) { - if (!isAddTaskRunning() - && (backupTask == null || backupTask.isCompleted())) { - JsonExportTask exportTask = new JsonExportTask(context, null, false, true, null); - backupTask = exportTask.launch(); - return true; + @Synchronized + fun tryBackupTask(context: Context): Boolean { + val backupTask = backupTask + if (!isAddTaskRunning + && (backupTask == null || backupTask.isCompleted)) { + val exportTask = JsonExportTask(context, null, false, true, null) + this.backupTask = exportTask.launch() + return true } - return false; + return false } - public synchronized void releaseBackupTaskRef() { - backupTask = null; // clear reference to avoid holding on to task context + @Synchronized + fun releaseBackupTaskRef() { + backupTask = null // clear reference to avoid holding on to task context } /** - * Schedules a {@link LatestEpisodeUpdateTask} for all shows + * Schedules a [LatestEpisodeUpdateTask] for all shows * if no other one of this type is currently running. */ @MainThread - public synchronized void tryNextEpisodeUpdateTask(Context context) { + @Synchronized + fun tryNextEpisodeUpdateTask(context: Context) { + val nextEpisodeUpdateTask = nextEpisodeUpdateTask if (nextEpisodeUpdateTask == null - || nextEpisodeUpdateTask.getStatus() == AsyncTask.Status.FINISHED) { - nextEpisodeUpdateTask = new LatestEpisodeUpdateTask(context); - nextEpisodeUpdateTask.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR); + || nextEpisodeUpdateTask.status == AsyncTask.Status.FINISHED) { + LatestEpisodeUpdateTask(context) + .also { this.nextEpisodeUpdateTask = it } + .executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) } } - public synchronized void releaseNextEpisodeUpdateTaskRef() { - nextEpisodeUpdateTask = null; // clear reference to avoid holding on to task context + @JvmStatic + @Synchronized + fun releaseNextEpisodeUpdateTaskRef() { + nextEpisodeUpdateTask = null // clear reference to avoid holding on to task context } + } From a5c9837e9743dda6e2efa9db50d59df6f9912501 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 14:02:20 +0200 Subject: [PATCH 10/13] AddShowTask: make it use its own data class --- .../seriesguide/history/HistoryActivity.kt | 12 +-------- .../seriesguide/shows/ShowsActivityImpl.kt | 10 +------- .../shows/search/SearchActivityImpl.kt | 10 +------- .../search/discover/AddShowDialogFragment.kt | 22 +++++----------- .../shows/search/discover/AddShowPopupMenu.kt | 5 +++- .../discover/ItemAddShowClickListener.kt | 5 +++- .../discover/ShowsDiscoverPagingActivity.kt | 7 +----- .../search/discover/ShowsTraktActivity.kt | 7 +----- .../shows/search/discover/TraktAddFragment.kt | 13 +++++++--- .../search/similar/SimilarShowsActivity.kt | 9 +------ .../seriesguide/shows/tools/AddShowTask.kt | 25 +++++++++++++------ .../shows/tools/AddUpdateShowTools.kt | 10 +++----- .../seriesguide/sync/HexagonShowSync.kt | 24 ++++++++++-------- .../seriesguide/sync/HexagonSync.java | 8 +++--- .../seriesguide/util/TaskManager.kt | 8 +++--- 15 files changed, 74 insertions(+), 101 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt index 44304dd30a..f6d0af2530 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/history/HistoryActivity.kt @@ -7,18 +7,15 @@ import android.os.Bundle import androidx.fragment.app.Fragment import com.battlelancer.seriesguide.BuildConfig import com.battlelancer.seriesguide.R -import com.battlelancer.seriesguide.shows.search.discover.AddShowDialogFragment.OnAddShowListener -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.ui.BaseActivity import com.battlelancer.seriesguide.ui.SinglePaneActivity -import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.commitReorderingAllowed import timber.log.Timber /** * Displays history of watched episodes or movies. */ -class HistoryActivity : BaseActivity(), OnAddShowListener { +class HistoryActivity : BaseActivity() { interface InitBundle { companion object { @@ -60,13 +57,6 @@ class HistoryActivity : BaseActivity(), OnAddShowListener { supportActionBar?.setDisplayHomeAsUpEnabled(true) } - /** - * Called if the user adds a show from a trakt stream fragment. - */ - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - companion object { const val EPISODES_LOADER_ID = 100 const val MOVIES_LOADER_ID = 101 diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt index 27af1af7da..03ba54be6f 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt @@ -23,7 +23,6 @@ import com.battlelancer.seriesguide.shows.calendar.UpcomingFragment import com.battlelancer.seriesguide.shows.episodes.EpisodesActivity import com.battlelancer.seriesguide.shows.history.ShowsHistoryFragment import com.battlelancer.seriesguide.shows.search.discover.AddShowDialogFragment -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.search.discover.ShowsDiscoverFragment import com.battlelancer.seriesguide.shows.search.discover.ShowsDiscoverPagingActivity import com.battlelancer.seriesguide.sync.AccountUtils @@ -47,7 +46,7 @@ import kotlinx.coroutines.launch * Provides the apps main screen, displays tabs for shows, discover, history, * recent and upcoming episodes. Runs upgrade code and checks billing state. */ -open class ShowsActivityImpl : BaseTopActivity(), AddShowDialogFragment.OnAddShowListener { +open class ShowsActivityImpl : BaseTopActivity() { private lateinit var tabsAdapter: TabStripAdapter private lateinit var viewPager: ViewPager2 @@ -332,13 +331,6 @@ open class ShowsActivityImpl : BaseTopActivity(), AddShowDialogFragment.OnAddSho return keyCode == KeyEvent.KEYCODE_BACK } - /** - * Called if the user adds a show from a trakt stream fragment. - */ - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - override val snackbarParentView: View get() = findViewById(R.id.coordinatorLayoutShows) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt index 97d66f3398..56d051caf3 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/SearchActivityImpl.kt @@ -14,12 +14,9 @@ import android.view.inputmethod.EditorInfo import androidx.viewpager2.widget.ViewPager2 import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.databinding.ActivitySearchBinding -import com.battlelancer.seriesguide.shows.search.discover.AddShowDialogFragment -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.ui.BaseMessageActivity import com.battlelancer.seriesguide.ui.TabStripAdapter import com.battlelancer.seriesguide.util.TabClickEvent -import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.ThemeUtils import com.battlelancer.seriesguide.util.ViewTools import com.google.android.gms.actions.SearchIntents @@ -33,7 +30,7 @@ import org.greenrobot.eventbus.EventBus * When [SearchManager.APP_DATA] contains a [EpisodeSearchFragment.ARG_SHOW_TITLE] switches to the * episodes tab. */ -open class SearchActivityImpl : BaseMessageActivity(), AddShowDialogFragment.OnAddShowListener { +open class SearchActivityImpl : BaseMessageActivity() { private lateinit var binding: ActivitySearchBinding @@ -193,11 +190,6 @@ open class SearchActivityImpl : BaseMessageActivity(), AddShowDialogFragment.OnA EventBus.getDefault().removeStickyEvent(SearchQueryEvent::class.java) } - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - - override val snackbarParentView: View get() = findViewById(R.id.coordinatorLayoutSearch) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt index e262322fea..c02914a576 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt @@ -19,6 +19,7 @@ import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.databinding.DialogAddshowBinding import com.battlelancer.seriesguide.shows.ShowsSettings import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsFragment +import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.shows.tools.ShowStatus import com.battlelancer.seriesguide.streaming.StreamingSearch import com.battlelancer.seriesguide.ui.OverviewActivity @@ -28,6 +29,7 @@ import com.battlelancer.seriesguide.util.LanguageTools import com.battlelancer.seriesguide.util.RatingsTools.initialize import com.battlelancer.seriesguide.util.RatingsTools.setRatingValues import com.battlelancer.seriesguide.util.ServiceUtils +import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.TextTools import com.battlelancer.seriesguide.util.TimeTools import com.battlelancer.seriesguide.util.ViewTools @@ -47,12 +49,7 @@ import timber.log.Timber */ class AddShowDialogFragment : AppCompatDialogFragment() { - interface OnAddShowListener { - fun onAddShow(show: SearchResult) - } - private var binding: DialogAddshowBinding? = null - private lateinit var addShowListener: OnAddShowListener private var showTmdbId: Int = 0 private lateinit var languageCode: String private val model by viewModels { @@ -61,12 +58,6 @@ class AddShowDialogFragment : AppCompatDialogFragment() { override fun onAttach(context: Context) { super.onAttach(context) - try { - addShowListener = context as OnAddShowListener - } catch (e: ClassCastException) { - throw ClassCastException("$context must implement OnAddShowListener") - } - showTmdbId = requireArguments().getInt(ARG_INT_SHOW_TMDBID) val languageCodeOrNull = requireArguments().getString(ARG_STRING_LANGUAGE_CODE) if (languageCodeOrNull.isNullOrEmpty()) { @@ -236,11 +227,10 @@ class AddShowDialogFragment : AppCompatDialogFragment() { binding.buttonPositive.setText(R.string.action_shows_add) binding.buttonPositive.setOnClickListener { EventBus.getDefault().post(OnAddingShowEvent(showTmdbId)) - addShowListener.onAddShow(SearchResult().also { - it.tmdbId = showTmdbId - it.title = show.title - it.language = languageCode - }) + TaskManager.performAddTask( + requireContext(), + AddShowTask.Show(showTmdbId, languageCode, show.title) + ) dismiss() } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt index d227cc2cdc..85ed93379c 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.View import androidx.appcompat.widget.PopupMenu import com.battlelancer.seriesguide.R +import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.tasks.AddShowToWatchlistTask import com.battlelancer.seriesguide.util.tasks.RemoveShowFromWatchlistTask @@ -39,7 +40,9 @@ class AddShowPopupMenu( R.id.menu_action_add_show_add -> { // post so other fragments can display a progress indicator for that show EventBus.getDefault().post(OnAddingShowEvent(show.tmdbId)) - TaskManager.performAddTask(context, show) + TaskManager.performAddTask( + context, AddShowTask.Show(show.tmdbId, show.language!!, show.title) + ) true } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt index a06473950f..b09a1169a3 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt @@ -9,6 +9,7 @@ import androidx.fragment.app.FragmentManager import androidx.lifecycle.Lifecycle import androidx.lifecycle.coroutineScope import com.battlelancer.seriesguide.provider.SgRoomDatabase +import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.traktapi.TraktCredentials import com.battlelancer.seriesguide.ui.OverviewActivity import com.battlelancer.seriesguide.util.TaskManager @@ -45,7 +46,9 @@ open class ItemAddShowClickListener( override fun onAddClick(item: SearchResult) { EventBus.getDefault().post(OnAddingShowEvent(item.tmdbId)) - TaskManager.performAddTask(context, item) + TaskManager.performAddTask( + context, AddShowTask.Show(item.tmdbId, item.language!!, item.title) + ) } override fun onMoreOptionsClick(view: View, show: SearchResult) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt index 8b3c0ebc6f..b47045e116 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingActivity.kt @@ -15,7 +15,6 @@ import com.battlelancer.seriesguide.shows.search.discover.ShowsDiscoverPagingAct import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsActivity import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsFragment import com.battlelancer.seriesguide.ui.BaseMessageActivity -import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.ThemeUtils import com.battlelancer.seriesguide.util.commitReorderingAllowed @@ -26,7 +25,7 @@ import com.battlelancer.seriesguide.util.commitReorderingAllowed * If launched with [intentLink] the search bar can be shown with a menu item and hidden by * going up or back. */ -class ShowsDiscoverPagingActivity : BaseMessageActivity(), AddShowDialogFragment.OnAddShowListener { +class ShowsDiscoverPagingActivity : BaseMessageActivity() { // Re-using layout of movies as filter chips are currently identical lateinit var binding: ActivityMoviesSearchBinding @@ -73,10 +72,6 @@ class ShowsDiscoverPagingActivity : BaseMessageActivity(), AddShowDialogFragment return intent.getStringExtra(EXTRA_QUERY) } - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - companion object { private const val EXTRA_LINK = "link" private const val EXTRA_QUERY = "query" diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt index 43689b48d9..eb19ce3c5c 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsTraktActivity.kt @@ -11,14 +11,13 @@ import com.battlelancer.seriesguide.databinding.ActivityTraktShowsBinding import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsActivity import com.battlelancer.seriesguide.shows.search.similar.SimilarShowsFragment import com.battlelancer.seriesguide.ui.BaseMessageActivity -import com.battlelancer.seriesguide.util.TaskManager import com.battlelancer.seriesguide.util.ThemeUtils import com.battlelancer.seriesguide.util.commitReorderingAllowed /** * Hosts [TraktAddFragment] configured by [DiscoverShowsLink]. */ -class ShowsTraktActivity : BaseMessageActivity(), AddShowDialogFragment.OnAddShowListener { +class ShowsTraktActivity : BaseMessageActivity() { lateinit var binding: ActivityTraktShowsBinding @@ -61,10 +60,6 @@ class ShowsTraktActivity : BaseMessageActivity(), AddShowDialogFragment.OnAddSho setTitle(link.titleRes) } - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - companion object { const val EXTRA_LINK = "LINK" const val TRAKT_BASE_LOADER_ID = 200 diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt index 9b4308dfe4..ffe13b603d 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt @@ -169,16 +169,23 @@ class TraktAddFragment : Fragment() { if (itemId == R.id.menu_add_all) { val searchResults = searchResults if (searchResults != null) { - val showsToAdd = LinkedList() + val showsToAdd = LinkedList() // only include shows not already added for (result in searchResults) { if (result.state == SearchResult.STATE_ADD) { - showsToAdd.add(result) + showsToAdd.add( + AddShowTask.Show( + result.tmdbId, + result.language!!, + result.title + ) + ) result.state = SearchResult.STATE_ADDING } } EventBus.getDefault().post(OnAddingShowEvent()) - TaskManager.performAddTask(requireContext(), showsToAdd, + TaskManager.performAddTask( + requireContext(), showsToAdd, isSilentMode = false, isMergingShows = false ) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt index 57536eaefd..9adfa427a2 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt @@ -8,12 +8,9 @@ import android.content.Intent import android.os.Bundle import androidx.fragment.app.Fragment import com.battlelancer.seriesguide.R -import com.battlelancer.seriesguide.shows.search.discover.AddShowDialogFragment -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.ui.BaseSimilarActivity -import com.battlelancer.seriesguide.util.TaskManager -class SimilarShowsActivity : BaseSimilarActivity(), AddShowDialogFragment.OnAddShowListener { +class SimilarShowsActivity : BaseSimilarActivity() { override val liftOnScrollTargetViewId: Int = SimilarShowsFragment.liftOnScrollTargetViewId override val titleStringRes: Int = R.string.title_similar_shows @@ -29,10 +26,6 @@ class SimilarShowsActivity : BaseSimilarActivity(), AddShowDialogFragment.OnAddS } } - override fun onAddShow(show: SearchResult) { - TaskManager.performAddTask(this, show) - } - companion object { fun intent(context: Context, showTmdbId: Int, showTitle: String?): Intent { return Intent(context, SimilarShowsActivity::class.java) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt index 7b84b72f13..236e80f13a 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddShowTask.kt @@ -13,7 +13,6 @@ import com.battlelancer.seriesguide.SgApp import com.battlelancer.seriesguide.backend.settings.HexagonSettings import com.battlelancer.seriesguide.backend.settings.HexagonSettings.isEnabled import com.battlelancer.seriesguide.provider.SeriesGuideDatabase -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.tools.AddUpdateShowTools.ShowResult import com.battlelancer.seriesguide.sync.HexagonEpisodeSync import com.battlelancer.seriesguide.traktapi.TraktCredentials.Companion.get @@ -34,11 +33,21 @@ import java.util.LinkedList */ class AddShowTask( context: Context, - shows: List, + shows: List, isSilentMode: Boolean, isMergingShows: Boolean ) : AsyncTask() { + /** + * [tmdbId] and [languageCode] are passed to [AddUpdateShowTools.addShow]. The [title] is only + * used for notifying the user, so it can be empty if the task is running in silent mode. + */ + data class Show( + val tmdbId: Int, + val languageCode: String, + val title: String + ) + class OnShowAddedEvent private constructor( /** * Is -1 if add task was aborted. @@ -98,7 +107,7 @@ class AddShowTask( @SuppressLint("StaticFieldLeak") private val context: Context = context.applicationContext - private val addQueue = LinkedList() + private val addQueue = LinkedList() private var isFinishedAddingShows = false private var isSilentMode: Boolean @@ -115,7 +124,7 @@ class AddShowTask( * is finishing up. Create a new one instead. */ fun addShows( - show: List, + shows: List, isSilentMode: Boolean, isMergingShows: Boolean ): Boolean { @@ -126,7 +135,7 @@ class AddShowTask( this.isSilentMode = isSilentMode // never reset isMergingShows once true, so merged flag is correctly set on completion this.isMergingShows = this.isMergingShows || isMergingShows - addQueue.addAll(show) + addQueue.addAll(shows) Timber.d("addShows: added shows to queue.") return true } @@ -204,8 +213,10 @@ class AddShowTask( } val addResult = showTools.addShow( - nextShow.tmdbId, nextShow.language, - traktCollection, traktWatched, hexagonEpisodeSync + nextShow.tmdbId, + nextShow.languageCode, + traktCollection, traktWatched, + hexagonEpisodeSync ) when (addResult) { ShowResult.SUCCESS -> { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddUpdateShowTools.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddUpdateShowTools.kt index bfa320381d..048a9d6364 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddUpdateShowTools.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/tools/AddUpdateShowTools.kt @@ -79,7 +79,7 @@ class AddUpdateShowTools @Inject constructor( fun addShow( showTmdbId: Int, - desiredLanguage: String?, + languageCode: String, traktCollection: Map?, traktWatched: Map?, hexagonEpisodeSync: HexagonEpisodeSync @@ -89,9 +89,7 @@ class AddUpdateShowTools @Inject constructor( return ShowResult.IN_DATABASE } - val language = desiredLanguage ?: LanguageTools.LANGUAGE_EN - - val showDetails = getShowTools.getShowDetails(showTmdbId, language) + val showDetails = getShowTools.getShowDetails(showTmdbId, languageCode) .getOrElse { return it.toShowResult() } val show = showDetails.show!! @@ -160,7 +158,7 @@ class AddUpdateShowTools @Inject constructor( showId, season.number, seasonId, - language, + languageCode, null, null ).getOrElse { return@runInTransaction ShowResult.TMDB_ERROR } @@ -185,7 +183,7 @@ class AddUpdateShowTools @Inject constructor( // updates the language, so the show will be auto-added on other connected devices. val cloudShow = SgCloudShow() cloudShow.tmdbId = showTmdbId - cloudShow.language = language + cloudShow.language = languageCode cloudShow.isRemoved = false // Prevent losing restored properties from a legacy Cloud show (see // hexagonTools.get().getShow used above) by always sending them. diff --git a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonShowSync.kt b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonShowSync.kt index f23b62cc12..fcf97891cd 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonShowSync.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonShowSync.kt @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2017-2023 Uwe Trottmann +// Copyright 2017-2024 Uwe Trottmann package com.battlelancer.seriesguide.sync @@ -9,8 +9,9 @@ import com.battlelancer.seriesguide.backend.HexagonTools import com.battlelancer.seriesguide.backend.settings.HexagonSettings import com.battlelancer.seriesguide.modules.ApplicationContext import com.battlelancer.seriesguide.provider.SgRoomDatabase +import com.battlelancer.seriesguide.shows.ShowsSettings import com.battlelancer.seriesguide.shows.database.SgShow2CloudUpdate -import com.battlelancer.seriesguide.shows.search.discover.SearchResult +import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.tmdbapi.TmdbTools2 import com.battlelancer.seriesguide.util.Errors.Companion.logAndReportHexagon import com.battlelancer.seriesguide.util.LanguageTools @@ -42,7 +43,7 @@ class HexagonShowSync @Inject constructor( */ fun download( tmdbIdsToShowIds: Map, - toAdd: HashMap, + toAdd: HashMap, hasMergedShows: Boolean ): Boolean { val updates: MutableList = ArrayList() @@ -86,7 +87,7 @@ class HexagonShowSync @Inject constructor( updates: MutableList, toUpdate: MutableSet, removed: MutableSet, - toAdd: HashMap, + toAdd: HashMap, tmdbIdsToShowIds: Map, hasMergedShows: Boolean, lastSyncTime: DateTime @@ -158,7 +159,7 @@ class HexagonShowSync @Inject constructor( updates: MutableList, toUpdate: MutableSet, removed: MutableSet, - toAdd: HashMap, + toAdd: HashMap, tmdbIdsToShowIds: Map ): Boolean { var cursor: String? = null @@ -255,11 +256,12 @@ class HexagonShowSync @Inject constructor( updates: MutableList, toUpdate: MutableSet, removed: MutableSet, - toAdd: MutableMap, + toAdd: MutableMap, shows: List, tmdbIdsToShowIds: Map, mergeValues: Boolean ) { + val defaultLanguageCode = ShowsSettings.getShowsSearchLanguage(context) for (show in shows) { // schedule to add shows not in local database val showTmdbId = show.tmdbId ?: continue // Invalid data. @@ -275,10 +277,12 @@ class HexagonShowSync @Inject constructor( continue } if (!toAdd.containsKey(showTmdbId)) { - val item = SearchResult() - item.tmdbId = showTmdbId - item.language = show.language?.let { LanguageTools.mapLegacyShowCode(it) } - item.title = "" + val item = AddShowTask.Show( + tmdbId = showTmdbId, + languageCode = show.language?.let { LanguageTools.mapLegacyShowCode(it) } + ?: defaultLanguageCode, + title = "" + ) toAdd[showTmdbId] = item } } else if (!toUpdate.contains(showIdOrNull)) { diff --git a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java index 629ee061db..bc9f4880ee 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java +++ b/app/src/main/java/com/battlelancer/seriesguide/sync/HexagonSync.java @@ -9,11 +9,11 @@ import com.battlelancer.seriesguide.SgApp; import com.battlelancer.seriesguide.backend.HexagonTools; import com.battlelancer.seriesguide.backend.settings.HexagonSettings; +import com.battlelancer.seriesguide.movies.tools.MovieTools; import com.battlelancer.seriesguide.provider.SgRoomDatabase; import com.battlelancer.seriesguide.shows.database.SgShow2Helper; import com.battlelancer.seriesguide.shows.database.SgShow2Ids; -import com.battlelancer.seriesguide.movies.tools.MovieTools; -import com.battlelancer.seriesguide.shows.search.discover.SearchResult; +import com.battlelancer.seriesguide.shows.tools.AddShowTask; import com.battlelancer.seriesguide.util.TaskManager; import com.uwetrottmann.androidutils.AndroidUtils; import java.util.HashMap; @@ -138,7 +138,7 @@ private HexagonResult syncShows(Map tmdbIdsToShowIds) { // download shows and apply property changes (if merging only overwrite some properties) HexagonShowSync showSync = new HexagonShowSync(context, hexagonTools); - HashMap newShows = new HashMap<>(); + HashMap newShows = new HashMap<>(); boolean downloadSuccessful = showSync.download(tmdbIdsToShowIds, newShows, hasMergedShows); if (!downloadSuccessful) { return new HexagonResult(false, false); @@ -155,7 +155,7 @@ private HexagonResult syncShows(Map tmdbIdsToShowIds) { // add new shows boolean addNewShows = !newShows.isEmpty(); if (addNewShows) { - List newShowsList = new LinkedList<>(newShows.values()); + List newShowsList = new LinkedList<>(newShows.values()); TaskManager.performAddTask(context, newShowsList, true, !hasMergedShows); } else if (!hasMergedShows) { // set shows as merged diff --git a/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt b/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt index c71035ba04..2d1c95ce47 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/util/TaskManager.kt @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023 Uwe Trottmann +// Copyright 2011-2024 Uwe Trottmann package com.battlelancer.seriesguide.util @@ -10,7 +10,6 @@ import android.widget.Toast import androidx.annotation.MainThread import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.dataliberation.JsonExportTask -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.tools.AddShowTask import com.battlelancer.seriesguide.shows.tools.LatestEpisodeUpdateTask import kotlinx.coroutines.Job @@ -27,8 +26,9 @@ object TaskManager { @MainThread @Synchronized - fun performAddTask(context: Context, show: SearchResult) = + fun performAddTask(context: Context, show: AddShowTask.Show) { performAddTask(context, listOf(show), isSilentMode = false, isMergingShows = false) + } /** * Schedule shows to be added to the database. @@ -41,7 +41,7 @@ object TaskManager { @Synchronized fun performAddTask( context: Context, - shows: List, + shows: List, isSilentMode: Boolean, isMergingShows: Boolean ) { From bc7429bcba4defae201fb53caee13d55e1c2f335 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 14:13:27 +0200 Subject: [PATCH 11/13] SimilarShowsFragment: use its own event data class --- .../movies/similar/SimilarMoviesActivity.kt | 6 +++--- .../movies/similar/SimilarMoviesFragment.kt | 4 ++-- .../search/discover/AddShowDialogFragment.kt | 10 ++++++---- .../search/similar/SimilarShowsActivity.kt | 4 ++-- .../search/similar/SimilarShowsFragment.kt | 10 +++++++--- .../seriesguide/ui/BaseSimilarActivity.kt | 19 +++++++++---------- 6 files changed, 29 insertions(+), 24 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesActivity.kt index 02ef160fd2..fff9ed5656 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesActivity.kt @@ -1,5 +1,5 @@ -// Copyright 2023 Uwe Trottmann // SPDX-License-Identifier: Apache-2.0 +// Copyright 2023-2024 Uwe Trottmann package com.battlelancer.seriesguide.movies.similar @@ -14,11 +14,11 @@ class SimilarMoviesActivity : BaseSimilarActivity() { override val liftOnScrollTargetViewId: Int = SimilarShowsFragment.liftOnScrollTargetViewId override val titleStringRes: Int = R.string.title_similar_movies - override fun createFragment(tmdbId: Int, title: String?): Fragment = + override fun createFragment(tmdbId: Int, title: String): Fragment = SimilarMoviesFragment.newInstance(tmdbId, title) companion object { - fun intent(context: Context, movieTmdbId: Int, title: String?): Intent { + fun intent(context: Context, movieTmdbId: Int, title: String): Intent { return Intent(context, SimilarMoviesActivity::class.java) .putExtras(movieTmdbId, title) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesFragment.kt index f628f13008..87e2f994f7 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/movies/similar/SimilarMoviesFragment.kt @@ -1,5 +1,5 @@ // SPDX-License-Identifier: Apache-2.0 -// Copyright 2023-2024 Uwe Trottmann +// Copyright 2019-2024 Uwe Trottmann package com.battlelancer.seriesguide.movies.similar @@ -119,7 +119,7 @@ class SimilarMoviesFragment : Fragment() { private const val ARG_TMDB_ID = "ARG_TMDB_ID" private const val ARG_TITLE = "ARG_TITLE" - fun newInstance(tmdbId: Int, title: String?): SimilarMoviesFragment { + fun newInstance(tmdbId: Int, title: String): SimilarMoviesFragment { return SimilarMoviesFragment().apply { arguments = Bundle().apply { putInt(ARG_TMDB_ID, tmdbId) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt index c02914a576..00651d8650 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt @@ -123,10 +123,12 @@ class AddShowDialogFragment : AppCompatDialogFragment() { val details = model.showDetails.value if (details?.show != null) { dismissAllowingStateLoss() - SimilarShowsFragment.displaySimilarShowsEventLiveData.postValue(SearchResult().also { - it.tmdbId = showTmdbId - it.title = details.show.title - }) + SimilarShowsFragment.displaySimilarShowsEventLiveData.postValue( + SimilarShowsFragment.SimilarShowEvent( + tmdbId = showTmdbId, + title = details.show.title + ) + ) } } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt index 9adfa427a2..965037a47a 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsActivity.kt @@ -14,7 +14,7 @@ class SimilarShowsActivity : BaseSimilarActivity() { override val liftOnScrollTargetViewId: Int = SimilarShowsFragment.liftOnScrollTargetViewId override val titleStringRes: Int = R.string.title_similar_shows - override fun createFragment(tmdbId: Int, title: String?): Fragment = + override fun createFragment(tmdbId: Int, title: String): Fragment = SimilarShowsFragment.newInstance(tmdbId, title) override fun onCreate(savedInstanceState: Bundle?) { @@ -27,7 +27,7 @@ class SimilarShowsActivity : BaseSimilarActivity() { } companion object { - fun intent(context: Context, showTmdbId: Int, showTitle: String?): Intent { + fun intent(context: Context, showTmdbId: Int, showTitle: String): Intent { return Intent(context, SimilarShowsActivity::class.java) .putExtras(showTmdbId, showTitle) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt index 8969297ce3..a4389433d5 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/similar/SimilarShowsFragment.kt @@ -18,7 +18,6 @@ import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.RecyclerView import com.battlelancer.seriesguide.R import com.battlelancer.seriesguide.shows.search.discover.BaseAddShowsFragment -import com.battlelancer.seriesguide.shows.search.discover.SearchResult import com.battlelancer.seriesguide.shows.search.discover.ShowsDiscoverPagingActivity import com.battlelancer.seriesguide.traktapi.TraktCredentials import com.battlelancer.seriesguide.ui.AutoGridLayoutManager @@ -151,6 +150,11 @@ class SimilarShowsFragment : BaseAddShowsFragment() { similarShowsViewModel.setStateForTmdbId(showTmdbId, newState) } + data class SimilarShowEvent( + val tmdbId: Int, + val title: String + ) + companion object { val liftOnScrollTargetViewId = R.id.recyclerViewShowsSimilar @@ -159,9 +163,9 @@ class SimilarShowsFragment : BaseAddShowsFragment() { private const val MENU_ITEM_SEARCH_ID = 1 @JvmStatic - val displaySimilarShowsEventLiveData = SingleLiveEvent() + val displaySimilarShowsEventLiveData = SingleLiveEvent() - fun newInstance(showTmdbId: Int, showTitle: String?): SimilarShowsFragment { + fun newInstance(showTmdbId: Int, showTitle: String): SimilarShowsFragment { return SimilarShowsFragment().apply { arguments = Bundle().apply { putInt(ARG_SHOW_TMDB_ID, showTmdbId) diff --git a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt index 901c7767d6..2f01c98bb3 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/ui/BaseSimilarActivity.kt @@ -18,7 +18,7 @@ abstract class BaseSimilarActivity : BaseMessageActivity() { abstract val liftOnScrollTargetViewId: Int abstract val titleStringRes: Int - abstract fun createFragment(tmdbId: Int, title: String?): Fragment + abstract fun createFragment(tmdbId: Int, title: String): Fragment private lateinit var binding: ActivitySinglepaneBinding override fun onCreate(savedInstanceState: Bundle?) { @@ -28,14 +28,13 @@ abstract class BaseSimilarActivity : BaseMessageActivity() { liftOnScrollTargetViewId setupActionBar() - val tmdbId = intent.getIntExtra(EXTRA_TMDB_ID, 0) - if (tmdbId <= 0) { - finish() - return - } - - val title = intent.getStringExtra(EXTRA_TITLE) if (savedInstanceState == null) { + val tmdbId = intent.getIntExtra(EXTRA_TMDB_ID, 0) + val title = intent.getStringExtra(EXTRA_TITLE) + if (tmdbId <= 0 || title == null) { + finish() + return + } addFragment(tmdbId, title) } } @@ -50,7 +49,7 @@ abstract class BaseSimilarActivity : BaseMessageActivity() { fun addFragment( tmdbId: Int, - title: String?, + title: String, addToBackStack: Boolean = false ) { val fragment = createFragment(tmdbId, title) @@ -71,7 +70,7 @@ abstract class BaseSimilarActivity : BaseMessageActivity() { private const val EXTRA_TMDB_ID = "EXTRA_TMDB_ID" private const val EXTRA_TITLE = "EXTRA_TITLE" - fun Intent.putExtras(showTmdbId: Int, title: String?): Intent { + fun Intent.putExtras(showTmdbId: Int, title: String): Intent { return this .putExtra(EXTRA_TMDB_ID, showTmdbId) .putExtra(EXTRA_TITLE, title) From 0f79c09f0b6f1cbaca35a5eb65c200884a0512e0 Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 14:31:16 +0200 Subject: [PATCH 12/13] AddShowDialogFragment: drop use of SearchResult class, enforce language --- .../history/UserEpisodeStreamFragment.kt | 6 +++- .../seriesguide/shows/ShowsActivityImpl.kt | 4 +-- .../shows/history/ShowsHistoryFragment.kt | 2 +- .../search/discover/AddShowDialogFragment.kt | 32 +++++++------------ .../discover/ItemAddShowClickListener.kt | 2 +- .../discover/ShowsDiscoverPagingFragment.kt | 2 +- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/history/UserEpisodeStreamFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/history/UserEpisodeStreamFragment.kt index ec4539ad45..27276d64c4 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/history/UserEpisodeStreamFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/history/UserEpisodeStreamFragment.kt @@ -81,7 +81,11 @@ class UserEpisodeStreamFragment : StreamFragment() { ) } else { // Offer to add the show if not in database. - AddShowDialogFragment.show(parentFragmentManager, showTmdbId) + AddShowDialogFragment.show( + requireContext(), + parentFragmentManager, + showTmdbId + ) } } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt index 03ba54be6f..4520f96a7b 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/ShowsActivityImpl.kt @@ -153,7 +153,7 @@ open class ShowsActivityImpl : BaseTopActivity() { } } else { // Show not added, offer to. - AddShowDialogFragment.show(supportFragmentManager, showTmdbId) + AddShowDialogFragment.show(this, supportFragmentManager, showTmdbId) } } } else if (Intents.ACTION_VIEW_SHOW == action) { @@ -169,7 +169,7 @@ open class ShowsActivityImpl : BaseTopActivity() { viewIntent = OverviewActivity.intentShow(this, showId) } else { // no such show, offer to add it - AddShowDialogFragment.show(supportFragmentManager, showTmdbId) + AddShowDialogFragment.show(this, supportFragmentManager, showTmdbId) } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/history/ShowsHistoryFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/history/ShowsHistoryFragment.kt index f2d9e42189..0a0cce89a4 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/history/ShowsHistoryFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/history/ShowsHistoryFragment.kt @@ -361,7 +361,7 @@ class ShowsHistoryFragment : Fragment() { showDetails(view, episodeRowId) } else if (showTmdbId != null && showTmdbId > 0) { // episode missing: show likely not in database, suggest adding it - AddShowDialogFragment.show(parentFragmentManager, showTmdbId) + AddShowDialogFragment.show(requireContext(), parentFragmentManager, showTmdbId) } } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt index 00651d8650..5d2d9c9141 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowDialogFragment.kt @@ -59,13 +59,8 @@ class AddShowDialogFragment : AppCompatDialogFragment() { override fun onAttach(context: Context) { super.onAttach(context) showTmdbId = requireArguments().getInt(ARG_INT_SHOW_TMDBID) - val languageCodeOrNull = requireArguments().getString(ARG_STRING_LANGUAGE_CODE) - if (languageCodeOrNull.isNullOrEmpty()) { - // Use search language. - this.languageCode = ShowsSettings.getShowsSearchLanguage(context) - } else { - this.languageCode = languageCodeOrNull - } + languageCode = requireArguments().getString(ARG_STRING_LANGUAGE_CODE) + ?: throw IllegalArgumentException("Language code must not be null") } override fun onCreate(savedInstanceState: Bundle?) { @@ -300,32 +295,27 @@ class AddShowDialogFragment : AppCompatDialogFragment() { private const val ARG_STRING_LANGUAGE_CODE = "language" /** - * Display a [AddShowDialogFragment] for the given show. The language of the show should - * be set. + * Display an [AddShowDialogFragment] for the given show. The language of the show should + * be set, otherwise uses [ShowsSettings.getShowsSearchLanguage]. */ - @JvmStatic - fun show(fm: FragmentManager, show: SearchResult) { + fun show(fm: FragmentManager, showTmdbId: Int, languageCode: String) { // Replace any currently showing add dialog (do not add it to the back stack). val ft = fm.beginTransaction() val prev = fm.findFragmentByTag(TAG) if (prev != null) { ft.remove(prev) } - newInstance(show.tmdbId, show.language).safeShow(fm, ft, TAG) + newInstance(showTmdbId, languageCode).safeShow(fm, ft, TAG) } /** - * Display a [AddShowDialogFragment] for the given show. + * Display an [AddShowDialogFragment] for the given show. * - * Use if there is no actual search result but just an id available. Uses the search - * or fall back language. + * Use if there is just an id available. The language code is always + * [ShowsSettings.getShowsSearchLanguage]. */ - @JvmStatic - fun show(fm: FragmentManager, showTmdbId: Int) { - val fakeResult = SearchResult().apply { - tmdbId = showTmdbId - } - show(fm, fakeResult) + fun show(context: Context, fm: FragmentManager, showTmdbId: Int) { + show(fm, showTmdbId, ShowsSettings.getShowsSearchLanguage(context)) } private fun newInstance(showTmdbId: Int, languageCode: String?): AddShowDialogFragment { diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt index b09a1169a3..f4976ecb32 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt @@ -39,7 +39,7 @@ open class ItemAddShowClickListener( } } else { // Display more details in a dialog. - AddShowDialogFragment.show(fragmentManager, item) + AddShowDialogFragment.show(fragmentManager, item.tmdbId, item.language!!) } } } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt index e16757e313..f4d8a3be05 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ShowsDiscoverPagingFragment.kt @@ -311,7 +311,7 @@ class ShowsDiscoverPagingFragment : BaseAddShowsFragment() { val showTmdbId = TmdbIdExtractor(requireContext(), query).tryToExtract() if (showTmdbId > 0) { // found an id, display the add dialog - AddShowDialogFragment.show(parentFragmentManager, showTmdbId) + AddShowDialogFragment.show(requireContext(), parentFragmentManager, showTmdbId) } else { // no id, do a search instead searchEditText.setText(query) From 6d756bf36dae382f4891038e8b305e68c2a769ee Mon Sep 17 00:00:00 2001 From: Uwe Trottmann Date: Thu, 17 Oct 2024 14:37:37 +0200 Subject: [PATCH 13/13] SearchResult: convert to data class, enforce values --- .../shows/search/discover/AddShowPopupMenu.kt | 2 +- .../discover/ItemAddShowClickListener.kt | 4 +- .../search/discover/ItemAddShowViewHolder.kt | 5 +- .../shows/search/discover/SearchResult.kt | 33 +++++-------- .../discover/SearchResultDiffCallback.kt | 4 +- .../search/discover/SearchResultMapper.kt | 48 ++++++++++--------- .../shows/search/discover/TraktAddFragment.kt | 2 +- 7 files changed, 43 insertions(+), 55 deletions(-) diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt index 85ed93379c..695ae4110a 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/AddShowPopupMenu.kt @@ -41,7 +41,7 @@ class AddShowPopupMenu( // post so other fragments can display a progress indicator for that show EventBus.getDefault().post(OnAddingShowEvent(show.tmdbId)) TaskManager.performAddTask( - context, AddShowTask.Show(show.tmdbId, show.language!!, show.title) + context, AddShowTask.Show(show.tmdbId, show.languageCode, show.title) ) true } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt index f4976ecb32..77c5ccb115 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowClickListener.kt @@ -39,7 +39,7 @@ open class ItemAddShowClickListener( } } else { // Display more details in a dialog. - AddShowDialogFragment.show(fragmentManager, item.tmdbId, item.language!!) + AddShowDialogFragment.show(fragmentManager, item.tmdbId, item.languageCode) } } } @@ -47,7 +47,7 @@ open class ItemAddShowClickListener( override fun onAddClick(item: SearchResult) { EventBus.getDefault().post(OnAddingShowEvent(item.tmdbId)) TaskManager.performAddTask( - context, AddShowTask.Show(item.tmdbId, item.language!!, item.title) + context, AddShowTask.Show(item.tmdbId, item.languageCode, item.title) ) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt index 09c510fde7..bc17ba0a57 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/ItemAddShowViewHolder.kt @@ -74,10 +74,7 @@ class ItemAddShowViewHolder( } // poster - ImageTools.loadShowPosterUrlResizeCrop( - context, binding.imageViewAddPoster, - item.posterPath // actually the poster URL - ) + ImageTools.loadShowPosterUrlResizeCrop(context, binding.imageViewAddPoster, item.posterUrl) // context/long press listener and more options button val canBeAdded = item.state == SearchResult.STATE_ADD diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt index e8d8dfbd0a..3b4bad7ba6 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResult.kt @@ -1,31 +1,20 @@ // SPDX-License-Identifier: Apache-2.0 // Copyright 2018-2024 Uwe Trottmann + package com.battlelancer.seriesguide.shows.search.discover /** - * Holds a search result, used later for adding this show. Supplying a poster URL is optional. + * Holds a search result. Supplying a poster URL is optional. */ -class SearchResult { - var tmdbId: Int = 0 - - /** Two-letter ISO 639-1 language code plus ISO-3166-1 region tag. */ - var language: String? = null - var title: String = "" - var overview: String? = null - var posterPath: String? = null - var state: Int = 0 - - fun copy(): SearchResult { - val copy = SearchResult() - copy.tmdbId = tmdbId - copy.language = language - copy.title = title - copy.overview = overview - copy.posterPath = posterPath - copy.state = state - return copy - } - +data class SearchResult( + val tmdbId: Int, + /** Two-letter ISO 639-1 language code plus ISO-3166-1 region tag. */ + val languageCode: String, + val title: String, + val overview: String, + var posterUrl: String?, + var state: Int = STATE_ADD +) { companion object { const val STATE_ADD: Int = 0 const val STATE_ADDING: Int = 1 diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultDiffCallback.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultDiffCallback.kt index eaaca86437..176c1f3bd4 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultDiffCallback.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultDiffCallback.kt @@ -14,8 +14,8 @@ class SearchResultDiffCallback : DiffUtil.ItemCallback() { override fun areContentsTheSame(oldItem: SearchResult, newItem: SearchResult): Boolean = oldItem.state == newItem.state - && oldItem.language == newItem.language - && oldItem.posterPath == newItem.posterPath + && oldItem.languageCode == newItem.languageCode + && oldItem.posterUrl == newItem.posterUrl && oldItem.overview == newItem.overview } \ No newline at end of file diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt index 17be8dd2a7..a26865e1cb 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/SearchResultMapper.kt @@ -36,15 +36,15 @@ abstract class SearchResultMapper( // Is already in local database. searchResult.state = SearchResult.STATE_ADDED // Use the poster already fetched for it. - val posterOrNull = localShowsToPoster[searchResult.tmdbId] - if (posterOrNull != null) { - searchResult.posterPath = posterOrNull + val posterPathOrNull = localShowsToPoster[searchResult.tmdbId] + if (posterPathOrNull != null) { + searchResult.posterUrl = posterPathOrNull } } // It may take some time to build the image cache URL, so do this here instead of when // binding to the view. - searchResult.posterPath = buildPosterUrl(searchResult) + searchResult.posterUrl = buildPosterUrl(searchResult) searchResult } @@ -63,17 +63,18 @@ class TmdbSearchResultMapper( override fun mapToSearchResult(show: BaseTvShow): SearchResult? { val tmdbId = show.id ?: return null - return SearchResult().also { - it.tmdbId = tmdbId - it.title = show.name - it.overview = show.overview - it.language = languageCode - it.posterPath = show.poster_path - } + val name = show.name ?: return null + return SearchResult( + tmdbId = tmdbId, + title = name, + overview = show.overview ?: "", + languageCode = languageCode, + posterUrl = show.poster_path // temporarily store path + ) } override fun buildPosterUrl(searchResult: SearchResult): String? { - return ImageTools.tmdbOrTvdbPosterUrl(searchResult.posterPath, context) + return ImageTools.tmdbOrTvdbPosterUrl(searchResult.posterUrl, context) } } @@ -88,21 +89,22 @@ class TraktSearchResultMapper( override fun mapToSearchResult(show: BaseShow): SearchResult? { val traktShow = show.show - val tmdbId = traktShow?.ids?.tmdb - ?: return null // has no TMDB id - return SearchResult().also { - it.tmdbId = tmdbId - it.title = traktShow.title + val tmdbId = traktShow?.ids?.tmdb ?: return null // has no TMDB id + val title = traktShow.title ?: return null + return SearchResult( + tmdbId = tmdbId, + title = title, // Trakt might not return an overview, so use the year if available - it.overview = if (!traktShow.overview.isNullOrEmpty()) { + overview = if (!traktShow.overview.isNullOrEmpty()) { traktShow.overview } else if (traktShow.year != null) { traktShow.year!!.toString() } else { "" - } - it.language = languageCode - } + }, + languageCode = languageCode, + posterUrl = null // Trakt does not supply poster URLs + ) } /** @@ -113,9 +115,9 @@ class TraktSearchResultMapper( */ override fun buildPosterUrl(searchResult: SearchResult): String? { return ImageTools.posterUrlOrResolve( - searchResult.posterPath, + searchResult.posterUrl, searchResult.tmdbId, - searchResult.language, + searchResult.languageCode, context ) } diff --git a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt index ffe13b603d..b11e42804b 100644 --- a/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt +++ b/app/src/main/java/com/battlelancer/seriesguide/shows/search/discover/TraktAddFragment.kt @@ -176,7 +176,7 @@ class TraktAddFragment : Fragment() { showsToAdd.add( AddShowTask.Show( result.tmdbId, - result.language!!, + result.languageCode, result.title ) )