Search in sources :

Example 1 with OperationApplicationException

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

the class DBUtils method updateLatestEpisode.

/**
     * Update next episode field and unwatched episode count for the given show. If no show id is
     * passed, will update next episodes for all shows.
     *
     * @return If only one show was passed, the TVDb id of the new next episode. Otherwise -1.
     */
public static long updateLatestEpisode(Context context, Integer showTvdbIdToUpdate) {
    // get a list of shows and their last watched episodes
    Cursor shows;
    try {
        shows = context.getContentResolver().query(Shows.CONTENT_URI_WITH_LAST_EPISODE, LastWatchedEpisodeQuery.PROJECTION, showTvdbIdToUpdate != null ? Qualified.SHOWS_ID + "=" + showTvdbIdToUpdate : null, null, null);
    } catch (SQLiteException e) {
        shows = null;
        Timber.e(e, "updateLatestEpisode: show query failed.");
        postDatabaseError(e);
    }
    if (shows == null) {
        // abort completely on query failure
        Timber.e("Failed to update next episode values");
        return -1;
    }
    final List<String[]> showsLastEpisodes = new ArrayList<>();
    while (shows.moveToNext()) {
        showsLastEpisodes.add(new String[] { // 0
        shows.getString(LastWatchedEpisodeQuery.SHOW_TVDB_ID), // 1
        shows.getString(LastWatchedEpisodeQuery.LAST_EPISODE_TVDB_ID), // 2
        shows.getString(LastWatchedEpisodeQuery.LAST_EPISODE_SEASON), // 3
        shows.getString(LastWatchedEpisodeQuery.LAST_EPISODE_NUMBER), shows.getString(LastWatchedEpisodeQuery.LAST_EPISODE_FIRST_RELEASE_MS) });
    }
    shows.close();
    // pre-build next episode selection
    final boolean isNoReleasedEpisodes = DisplaySettings.isNoReleasedEpisodes(context);
    final String nextEpisodeSelection = buildNextEpisodeSelection(DisplaySettings.isHidingSpecials(context), isNoReleasedEpisodes);
    // build updated next episode values for each show
    int nextEpisodeTvdbId = -1;
    final ContentValues newShowValues = new ContentValues();
    final ArrayList<ContentProviderOperation> batch = new ArrayList<>();
    final String currentTime = String.valueOf(TimeTools.getCurrentTime(context));
    final boolean displayExactDate = DisplaySettings.isDisplayExactDate(context);
    DisplaySettings.preventSpoilers(context);
    for (String[] show : showsLastEpisodes) {
        // STEP 1: get last watched episode details
        final String showTvdbId = show[0];
        final String lastEpisodeTvdbId = show[1];
        String season = show[2];
        String number = show[3];
        String releaseTime = show[4];
        if (TextUtils.isEmpty(lastEpisodeTvdbId) || season == null || number == null || releaseTime == null) {
            // by default: no watched episodes, include all starting with special 0
            season = "-1";
            number = "-1";
            releaseTime = String.valueOf(Long.MIN_VALUE);
        }
        // STEP 2: get episode released closest afterwards; or at the same time,
        // but with a higher number
        final String[] selectionArgs;
        if (isNoReleasedEpisodes) {
            // restrict to episodes with future release date
            selectionArgs = new String[] { releaseTime, number, season, releaseTime, currentTime };
        } else {
            // restrict to episodes with any valid air date
            selectionArgs = new String[] { releaseTime, number, season, releaseTime };
        }
        Cursor next;
        try {
            next = context.getContentResolver().query(Episodes.buildEpisodesOfShowUri(showTvdbId), NextEpisodesQuery.PROJECTION, nextEpisodeSelection, selectionArgs, NextEpisodesQuery.SORTORDER);
        } catch (SQLiteException e) {
            next = null;
            Timber.e(e, "updateLatestEpisode: next episode query failed.");
            postDatabaseError(e);
        }
        if (next == null) {
            // abort completely on query failure
            Timber.e("Failed to update next episode values");
            return -1;
        }
        // STEP 3: build updated next episode values
        if (next.moveToFirst()) {
            final String nextEpisodeString;
            int seasonNumber = next.getInt(NextEpisodesQuery.SEASON);
            int episodeNumber = next.getInt(NextEpisodesQuery.NUMBER);
            if (DisplaySettings.preventSpoilers(context)) {
                // just the number, like '0x12'
                nextEpisodeString = TextTools.getEpisodeNumber(context, seasonNumber, episodeNumber);
            } else {
                // next episode text, like '0x12 Episode Name'
                nextEpisodeString = TextTools.getNextEpisodeString(context, seasonNumber, episodeNumber, next.getString(NextEpisodesQuery.TITLE));
            }
            // next release date text, e.g. "in 15 mins (Fri)"
            long releaseTimeNext = next.getLong(NextEpisodesQuery.FIRST_RELEASE_MS);
            Date actualRelease = TimeTools.applyUserOffset(context, releaseTimeNext);
            String dateTime = displayExactDate ? TimeTools.formatToLocalDateShort(context, actualRelease) : TimeTools.formatToLocalRelativeTime(context, actualRelease);
            final String nextReleaseDateString = context.getString(R.string.release_date_and_day, dateTime, TimeTools.formatToLocalDay(actualRelease));
            nextEpisodeTvdbId = next.getInt(NextEpisodesQuery.ID);
            newShowValues.put(Shows.NEXTEPISODE, nextEpisodeTvdbId);
            newShowValues.put(Shows.NEXTAIRDATEMS, releaseTimeNext);
            newShowValues.put(Shows.NEXTTEXT, nextEpisodeString);
            newShowValues.put(Shows.NEXTAIRDATETEXT, nextReleaseDateString);
        } else {
            // no next episode, set empty values
            nextEpisodeTvdbId = 0;
            newShowValues.put(Shows.NEXTEPISODE, "");
            newShowValues.put(Shows.NEXTAIRDATEMS, UNKNOWN_NEXT_RELEASE_DATE);
            newShowValues.put(Shows.NEXTTEXT, "");
            newShowValues.put(Shows.NEXTAIRDATETEXT, "");
        }
        next.close();
        // STEP 4: get remaining episodes count
        int unwatchedEpisodesCount = getUnwatchedEpisodesOfShow(context, showTvdbId);
        newShowValues.put(Shows.UNWATCHED_COUNT, unwatchedEpisodesCount);
        // update the show with the new next episode values
        batch.add(ContentProviderOperation.newUpdate(Shows.buildShowUri(showTvdbId)).withValues(newShowValues).build());
        newShowValues.clear();
    }
    try {
        DBUtils.applyInSmallBatches(context, batch);
    } catch (OperationApplicationException e) {
        Timber.e(e, "Failed to update next episode values");
        return -1;
    }
    return nextEpisodeTvdbId;
}
Also used : ContentValues(android.content.ContentValues) ContentProviderOperation(android.content.ContentProviderOperation) ArrayList(java.util.ArrayList) Cursor(android.database.Cursor) SQLiteException(android.database.sqlite.SQLiteException) Date(java.util.Date) OperationApplicationException(android.content.OperationApplicationException)

Example 2 with OperationApplicationException

use of android.content.OperationApplicationException 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 3 with OperationApplicationException

use of android.content.OperationApplicationException 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 4 with OperationApplicationException

use of android.content.OperationApplicationException 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 5 with OperationApplicationException

use of android.content.OperationApplicationException 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)

Aggregations

OperationApplicationException (android.content.OperationApplicationException)67 ContentProviderOperation (android.content.ContentProviderOperation)60 ArrayList (java.util.ArrayList)55 RemoteException (android.os.RemoteException)46 ContentProviderResult (android.content.ContentProviderResult)23 ContentValues (android.content.ContentValues)17 ContentResolver (android.content.ContentResolver)12 Cursor (android.database.Cursor)11 Uri (android.net.Uri)11 IOException (java.io.IOException)11 Intent (android.content.Intent)9 OCShare (com.owncloud.android.lib.resources.shares.OCShare)6 LinkedList (java.util.LinkedList)6 List (java.util.List)6 JSONArray (org.json.JSONArray)6 JSONException (org.json.JSONException)6 JSONObject (org.json.JSONObject)6 BroadcastReceiver (android.content.BroadcastReceiver)4 Context (android.content.Context)4 IntentFilter (android.content.IntentFilter)4