Search in sources :

Example 6 with ContentProviderOperation

use of android.content.ContentProviderOperation in project SeriesGuide by UweTrottmann.

the class TraktTools method downloadShowRatings.

/**
     * Downloads trakt show ratings and applies the latest ones to the database.
     *
     * <p> To apply all ratings, set {@link TraktSettings#KEY_LAST_SHOWS_RATED_AT} to 0.
     */
public UpdateResult downloadShowRatings(@Nullable DateTime ratedAt) {
    if (ratedAt == null) {
        Timber.e("downloadShowRatings: null rated_at");
        return UpdateResult.INCOMPLETE;
    }
    long lastRatedAt = TraktSettings.getLastShowsRatedAt(context);
    if (!ratedAt.isAfter(lastRatedAt)) {
        // not initial sync, no ratings have changed
        Timber.d("downloadShowRatings: no changes since %tF %tT", lastRatedAt, lastRatedAt);
        return UpdateResult.SUCCESS;
    }
    if (!TraktCredentials.get(context).hasCredentials()) {
        return UpdateResult.INCOMPLETE;
    }
    // download rated shows
    List<RatedShow> ratedShows;
    try {
        Response<List<RatedShow>> response = traktSync.get().ratingsShows(RatingsFilter.ALL, Extended.DEFAULT_MIN).execute();
        if (response.isSuccessful()) {
            ratedShows = response.body();
        } else {
            if (SgTrakt.isUnauthorized(context, response)) {
                return UpdateResult.INCOMPLETE;
            }
            SgTrakt.trackFailedRequest(context, "get show ratings", response);
            return UpdateResult.INCOMPLETE;
        }
    } catch (IOException e) {
        SgTrakt.trackFailedRequest(context, "get show ratings", e);
        return UpdateResult.INCOMPLETE;
    }
    if (ratedShows == null) {
        Timber.e("downloadShowRatings: null response");
        return UpdateResult.INCOMPLETE;
    }
    if (ratedShows.isEmpty()) {
        Timber.d("downloadShowRatings: no ratings on trakt");
        return UpdateResult.SUCCESS;
    }
    // trakt last activity rated_at timestamp is set after the rating timestamp
    // so include ratings that are a little older
    long ratedAtThreshold = lastRatedAt - 5 * DateUtils.MINUTE_IN_MILLIS;
    // go through ratings, latest first (trakt sends in that order)
    ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    for (RatedShow show : ratedShows) {
        if (show.rating == null || show.show == null || show.show.ids == null || show.show.ids.tvdb == null) {
            // skip, can't handle
            continue;
        }
        if (show.rated_at != null && show.rated_at.isBefore(ratedAtThreshold)) {
            // no need to apply older ratings again
            break;
        }
        // if a show does not exist, this update will do nothing
        ContentProviderOperation op = ContentProviderOperation.newUpdate(SeriesGuideContract.Shows.buildShowUri(show.show.ids.tvdb)).withValue(SeriesGuideContract.Shows.RATING_USER, show.rating.value).build();
        batch.add(op);
    }
    // apply database updates
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "downloadShowRatings: database update failed");
        return UpdateResult.INCOMPLETE;
    }
    // save last rated instant
    PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(TraktSettings.KEY_LAST_SHOWS_RATED_AT, ratedAt.getMillis()).commit();
    Timber.d("downloadShowRatings: success, last rated_at %tF %tT", ratedAt.getMillis(), ratedAt.getMillis());
    return UpdateResult.SUCCESS;
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) RatedShow(com.uwetrottmann.trakt5.entities.RatedShow) ArrayList(java.util.ArrayList) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) IOException(java.io.IOException) OperationApplicationException(android.content.OperationApplicationException)

Example 7 with ContentProviderOperation

use of android.content.ContentProviderOperation in project SeriesGuide by UweTrottmann.

the class TraktTools method processTraktSeasons.

/**
     * Sync the watched/collected episodes of the given trakt show with the local episodes. The
     * given show has to be watched/collected on trakt.
     *
     * @param isInitialSync If {@code true}, will upload watched/collected episodes that are not
     * watched/collected on trakt. If {@code false}, will set them not watched/collected (if not
     * skipped) to mirror the trakt episode.
     */
public int processTraktSeasons(boolean isInitialSync, int localShow, @NonNull BaseShow traktShow, @NonNull Flag flag) {
    HashMap<Integer, BaseSeason> traktSeasons = buildTraktSeasonsMap(traktShow.seasons);
    Cursor localSeasonsQuery = context.getContentResolver().query(SeriesGuideContract.Seasons.buildSeasonsOfShowUri(localShow), new String[] { SeriesGuideContract.Seasons._ID, SeriesGuideContract.Seasons.COMBINED }, null, null, null);
    if (localSeasonsQuery == null) {
        return FAILED;
    }
    final ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    List<SyncSeason> syncSeasons = new ArrayList<>();
    while (localSeasonsQuery.moveToNext()) {
        String seasonId = localSeasonsQuery.getString(0);
        int seasonNumber = localSeasonsQuery.getInt(1);
        if (traktSeasons.containsKey(seasonNumber)) {
            // season watched/collected on trakt
            if (!processTraktEpisodes(isInitialSync, seasonId, traktSeasons.get(seasonNumber), syncSeasons, flag)) {
                return FAILED;
            }
        } else {
            // season not watched/collected on trakt
            if (isInitialSync) {
                // schedule all watched/collected episodes of this season for upload
                SyncSeason syncSeason = buildSyncSeason(seasonId, seasonNumber, flag);
                if (syncSeason != null) {
                    syncSeasons.add(syncSeason);
                }
            } else {
                // set all watched/collected episodes of season not watched/collected
                batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Episodes.buildEpisodesOfSeasonUri(seasonId)).withSelection(flag.clearFlagSelection, null).withValue(flag.databaseColumn, flag.notFlaggedValue).build());
            }
        }
    }
    localSeasonsQuery.close();
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "Setting seasons unwatched failed.");
    }
    if (syncSeasons.size() > 0) {
        // upload watched/collected episodes for this show
        Integer showTraktId = ShowTools.getShowTraktId(context, localShow);
        if (showTraktId == null) {
            // show should have a trakt id, give up
            return FAILED;
        }
        return uploadEpisodes(showTraktId, syncSeasons, flag);
    } else {
        return SUCCESS;
    }
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) BaseSeason(com.uwetrottmann.trakt5.entities.BaseSeason) OperationApplicationException(android.content.OperationApplicationException) SyncSeason(com.uwetrottmann.trakt5.entities.SyncSeason)

Example 8 with ContentProviderOperation

use of android.content.ContentProviderOperation in project SeriesGuide by UweTrottmann.

the class TraktTools method downloadWatchedMovies.

/**
     * Downloads trakt movie watched flags and mirrors them in the local database. Does NOT upload
     * any flags (e.g. trakt is considered the truth).
     */
public UpdateResult downloadWatchedMovies(DateTime watchedAt) {
    if (watchedAt == null) {
        Timber.e("downloadWatchedMovies: null watched_at");
        return UpdateResult.INCOMPLETE;
    }
    long lastWatchedAt = TraktSettings.getLastMoviesWatchedAt(context);
    if (!watchedAt.isAfter(lastWatchedAt)) {
        // not initial sync, no watched flags have changed
        Timber.d("downloadWatchedMovies: no changes since %tF %tT", lastWatchedAt, lastWatchedAt);
        return UpdateResult.SUCCESS;
    }
    if (!TraktCredentials.get(context).hasCredentials()) {
        return UpdateResult.INCOMPLETE;
    }
    // download watched movies
    List<BaseMovie> watchedMovies;
    try {
        Response<List<BaseMovie>> response = traktSync.get().watchedMovies(Extended.DEFAULT_MIN).execute();
        if (response.isSuccessful()) {
            watchedMovies = response.body();
        } else {
            if (SgTrakt.isUnauthorized(context, response)) {
                return UpdateResult.INCOMPLETE;
            }
            SgTrakt.trackFailedRequest(context, "get watched movies", response);
            return UpdateResult.INCOMPLETE;
        }
    } catch (IOException e) {
        SgTrakt.trackFailedRequest(context, "get watched movies", e);
        return UpdateResult.INCOMPLETE;
    }
    if (watchedMovies == null) {
        Timber.e("downloadWatchedMovies: null response");
        return UpdateResult.INCOMPLETE;
    }
    if (watchedMovies.isEmpty()) {
        Timber.d("downloadWatchedMovies: no watched movies on trakt");
        return UpdateResult.SUCCESS;
    }
    // apply watched flags for all watched trakt movies that are in the local database
    ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    Set<Integer> localMovies = MovieTools.getMovieTmdbIdsAsSet(context);
    if (localMovies == null) {
        return UpdateResult.INCOMPLETE;
    }
    Set<Integer> unwatchedMovies = new HashSet<>(localMovies);
    for (BaseMovie movie : watchedMovies) {
        if (movie.movie == null || movie.movie.ids == null || movie.movie.ids.tmdb == null) {
            // required values are missing
            continue;
        }
        if (!localMovies.contains(movie.movie.ids.tmdb)) {
            // movie NOT in local database
            // add a shell entry for storing watched state
            batch.add(ContentProviderOperation.newInsert(SeriesGuideContract.Movies.CONTENT_URI).withValue(SeriesGuideContract.Movies.TMDB_ID, movie.movie.ids.tmdb).withValue(SeriesGuideContract.Movies.WATCHED, true).withValue(SeriesGuideContract.Movies.IN_COLLECTION, false).withValue(SeriesGuideContract.Movies.IN_WATCHLIST, false).build());
        } else {
            // movie IN local database
            // set movie watched
            batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Movies.buildMovieUri(movie.movie.ids.tmdb)).withValue(SeriesGuideContract.Movies.WATCHED, true).build());
            unwatchedMovies.remove(movie.movie.ids.tmdb);
        }
    }
    // remove watched flags from all remaining local movies
    for (Integer tmdbId : unwatchedMovies) {
        batch.add(ContentProviderOperation.newUpdate(SeriesGuideContract.Movies.buildMovieUri(tmdbId)).withValue(SeriesGuideContract.Movies.WATCHED, false).build());
    }
    // apply database updates
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "downloadWatchedMovies: updating watched flags failed");
        return UpdateResult.INCOMPLETE;
    }
    // save last watched instant
    PreferenceManager.getDefaultSharedPreferences(context).edit().putLong(TraktSettings.KEY_LAST_MOVIES_WATCHED_AT, watchedAt.getMillis()).commit();
    Timber.d("downloadWatchedMovies: success, last watched_at %tF %tT", watchedAt.getMillis(), watchedAt.getMillis());
    return UpdateResult.SUCCESS;
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) ArrayList(java.util.ArrayList) IOException(java.io.IOException) BaseMovie(com.uwetrottmann.trakt5.entities.BaseMovie) List(java.util.List) ArrayList(java.util.ArrayList) LinkedList(java.util.LinkedList) OperationApplicationException(android.content.OperationApplicationException) HashSet(java.util.HashSet)

Example 9 with ContentProviderOperation

use of android.content.ContentProviderOperation in project SeriesGuide by UweTrottmann.

the class ShowTools method removeShow.

/**
     * Removes a show and its seasons and episodes, including all images. Sends isRemoved flag to
     * Hexagon.
     *
     * @return One of {@link com.battlelancer.seriesguide.enums.NetworkResult}.
     */
public int removeShow(int showTvdbId) {
    if (HexagonSettings.isEnabled(app)) {
        if (!AndroidUtils.isNetworkConnected(app)) {
            return NetworkResult.OFFLINE;
        }
        // send to cloud
        sendIsRemoved(showTvdbId);
    }
    // remove database entries in stages, so if an earlier stage fails, user can at least try again
    // also saves memory by applying batches early
    // SEARCH DATABASE ENTRIES
    final Cursor episodes = app.getContentResolver().query(SeriesGuideContract.Episodes.buildEpisodesOfShowUri(showTvdbId), new String[] { SeriesGuideContract.Episodes._ID }, null, null, null);
    if (episodes == null) {
        // failed
        return Result.ERROR;
    }
    // need those for search entries
    List<String> episodeTvdbIds = new LinkedList<>();
    while (episodes.moveToNext()) {
        episodeTvdbIds.add(episodes.getString(0));
    }
    episodes.close();
    // remove episode search database entries
    final ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    for (String episodeTvdbId : episodeTvdbIds) {
        batch.add(ContentProviderOperation.newDelete(SeriesGuideContract.EpisodeSearch.buildDocIdUri(episodeTvdbId)).build());
    }
    try {
        DBUtils.applyInSmallBatches(app, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "Removing episode search entries failed");
        return Result.ERROR;
    }
    batch.clear();
    // ACTUAL ENTITY ENTRIES
    // remove episodes, seasons and show
    batch.add(ContentProviderOperation.newDelete(SeriesGuideContract.Episodes.buildEpisodesOfShowUri(showTvdbId)).build());
    batch.add(ContentProviderOperation.newDelete(SeriesGuideContract.Seasons.buildSeasonsOfShowUri(showTvdbId)).build());
    batch.add(ContentProviderOperation.newDelete(SeriesGuideContract.Shows.buildShowUri(showTvdbId)).build());
    try {
        DBUtils.applyInSmallBatches(app, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "Removing episodes, seasons and show failed");
        return Result.ERROR;
    }
    // make sure other loaders (activity, overview, details, search) are notified
    app.getContentResolver().notifyChange(SeriesGuideContract.Episodes.CONTENT_URI_WITHSHOW, null);
    app.getContentResolver().notifyChange(SeriesGuideContract.Shows.CONTENT_URI_FILTER, null);
    return Result.SUCCESS;
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) LinkedList(java.util.LinkedList) OperationApplicationException(android.content.OperationApplicationException)

Example 10 with ContentProviderOperation

use of android.content.ContentProviderOperation in project SeriesGuide by UweTrottmann.

the class ListsTools method doListsDatabaseUpdate.

private static boolean doListsDatabaseUpdate(Context context, List<SgList> lists, HashSet<String> localListIds, boolean hasMergedLists) {
    ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    for (SgList list : lists) {
        // add or update the list
        String listId = list.getListId();
        ContentProviderOperation.Builder builder = null;
        if (localListIds.contains(listId)) {
            // update
            if (hasMergedLists) {
                // only overwrite name and order if data was already merged
                // use case: user disconnected for a while, changed lists, then reconnects
                builder = ContentProviderOperation.newUpdate(SeriesGuideContract.Lists.buildListUri(listId));
            }
        } else {
            // insert
            builder = ContentProviderOperation.newInsert(SeriesGuideContract.Lists.CONTENT_URI).withValue(SeriesGuideContract.Lists.LIST_ID, listId);
        }
        if (builder != null) {
            builder.withValue(SeriesGuideContract.Lists.NAME, list.getName());
            if (list.getOrder() != null) {
                builder.withValue(SeriesGuideContract.Lists.ORDER, list.getOrder());
            }
            batch.add(builder.build());
        }
        // keep track of items not in the list on hexagon
        HashSet<String> listItemsToRemove = null;
        if (hasMergedLists) {
            listItemsToRemove = getListItemIds(context, listId);
            if (listItemsToRemove == null) {
                // list item query failed
                return false;
            }
        }
        // add or update items of the list
        List<SgListItem> listItems = list.getListItems();
        if (listItems != null) {
            for (SgListItem listItem : listItems) {
                String listItemId = listItem.getListItemId();
                String[] brokenUpId = SeriesGuideContract.ListItems.splitListItemId(listItemId);
                if (brokenUpId == null) {
                    // could not break up list item id
                    continue;
                }
                int itemTvdbId = -1;
                int itemType = -1;
                try {
                    itemTvdbId = Integer.parseInt(brokenUpId[0]);
                    itemType = Integer.parseInt(brokenUpId[1]);
                } catch (NumberFormatException ignored) {
                }
                if (itemTvdbId == -1 || !SeriesGuideContract.ListItems.isValidItemType(itemType)) {
                    // failed to extract item TVDB id or item type not known
                    continue;
                }
                // just insert the list item, if the id already exists it will be replaced
                builder = ContentProviderOperation.newInsert(SeriesGuideContract.ListItems.CONTENT_URI).withValue(SeriesGuideContract.ListItems.LIST_ITEM_ID, listItemId).withValue(SeriesGuideContract.ListItems.ITEM_REF_ID, itemTvdbId).withValue(SeriesGuideContract.ListItems.TYPE, itemType).withValue(SeriesGuideContract.Lists.LIST_ID, listId);
                batch.add(builder.build());
                if (hasMergedLists) {
                    // do not remove this list item
                    listItemsToRemove.remove(listItemId);
                }
            }
        }
        if (hasMergedLists) {
            // remove items no longer in the list
            for (String listItemId : listItemsToRemove) {
                builder = ContentProviderOperation.newDelete(SeriesGuideContract.ListItems.buildListItemUri(listItemId));
                batch.add(builder.build());
            }
        }
    }
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "doListsDatabaseUpdate: failed.");
        return false;
    }
    return true;
}
Also used : ContentProviderOperation(android.content.ContentProviderOperation) SgList(com.uwetrottmann.seriesguide.backend.lists.model.SgList) SgListItem(com.uwetrottmann.seriesguide.backend.lists.model.SgListItem) ArrayList(java.util.ArrayList) SuppressLint(android.annotation.SuppressLint) OperationApplicationException(android.content.OperationApplicationException)

Aggregations

ContentProviderOperation (android.content.ContentProviderOperation)75 ArrayList (java.util.ArrayList)53 OperationApplicationException (android.content.OperationApplicationException)34 Uri (android.net.Uri)27 ContentValues (android.content.ContentValues)25 RemoteException (android.os.RemoteException)22 Cursor (android.database.Cursor)14 ContentProviderResult (android.content.ContentProviderResult)13 ContentResolver (android.content.ContentResolver)9 LinkedList (java.util.LinkedList)8 IOException (java.io.IOException)7 JSONArray (org.json.JSONArray)6 JSONException (org.json.JSONException)6 JSONObject (org.json.JSONObject)6 Intent (android.content.Intent)5 List (java.util.List)5 SuppressLint (android.annotation.SuppressLint)4 BroadcastReceiver (android.content.BroadcastReceiver)4 Context (android.content.Context)4 IntentFilter (android.content.IntentFilter)4