/*
* Copyright (C) 2013 Simon Vig Therkildsen
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package net.simonvt.cathode.provider;
import android.content.ContentProviderOperation;
import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.content.OperationApplicationException;
import android.database.Cursor;
import android.os.RemoteException;
import java.util.ArrayList;
import java.util.List;
import net.simonvt.cathode.CathodeApp;
import net.simonvt.cathode.api.entity.Movie;
import net.simonvt.cathode.api.util.TimeUtils;
import net.simonvt.cathode.database.DatabaseUtils;
import net.simonvt.cathode.provider.DatabaseContract.MovieColumns;
import net.simonvt.cathode.provider.DatabaseContract.MovieGenreColumns;
import net.simonvt.cathode.provider.ProviderSchematic.MovieGenres;
import net.simonvt.cathode.provider.ProviderSchematic.Movies;
import net.simonvt.cathode.provider.generated.CathodeProvider;
import net.simonvt.cathode.util.TextUtils;
import net.simonvt.schematic.Cursors;
import timber.log.Timber;
public final class MovieDatabaseHelper {
private static volatile MovieDatabaseHelper instance;
public static MovieDatabaseHelper getInstance(Context context) {
if (instance == null) {
synchronized (MovieDatabaseHelper.class) {
if (instance == null) {
instance = new MovieDatabaseHelper(context.getApplicationContext());
}
}
}
return instance;
}
private static final Object LOCK_ID = new Object();
private Context context;
private ContentResolver resolver;
private MovieDatabaseHelper(Context context) {
this.context = context;
resolver = context.getContentResolver();
CathodeApp.inject(context, this);
}
public long getTraktId(long movieId) {
Cursor c = resolver.query(Movies.withId(movieId), new String[] {
MovieColumns.TRAKT_ID,
}, null, null, null);
long traktId = -1;
if (c.moveToFirst()) {
traktId = Cursors.getLong(c, MovieColumns.TRAKT_ID);
}
c.close();
return traktId;
}
public int getTmdbId(long movieId) {
Cursor c = resolver.query(Movies.withId(movieId), new String[] {
MovieColumns.TMDB_ID,
}, null, null, null);
int traktId = -1;
if (c.moveToFirst()) {
traktId = Cursors.getInt(c, MovieColumns.TMDB_ID);
}
c.close();
return traktId;
}
public long getId(long traktId) {
synchronized (LOCK_ID) {
Cursor c = resolver.query(Movies.MOVIES, new String[] {
MovieColumns.ID,
}, MovieColumns.TRAKT_ID + "=?", new String[] {
String.valueOf(traktId),
}, null);
long id = !c.moveToFirst() ? -1L : Cursors.getLong(c, MovieColumns.ID);
c.close();
return id;
}
}
public long getIdFromTmdb(int tmdbId) {
synchronized (LOCK_ID) {
Cursor c = resolver.query(Movies.MOVIES, new String[] {
MovieColumns.ID,
}, MovieColumns.TMDB_ID + "=?", new String[] {
String.valueOf(tmdbId),
}, null);
long id = !c.moveToFirst() ? -1L : Cursors.getLong(c, MovieColumns.ID);
c.close();
return id;
}
}
public boolean needsSync(long movieId) {
Cursor movie = null;
try {
movie = resolver.query(Movies.withId(movieId), new String[] {
MovieColumns.NEEDS_SYNC,
}, null, null, null);
if (movie.moveToFirst()) {
return Cursors.getBoolean(movie, MovieColumns.NEEDS_SYNC);
}
return false;
} finally {
if (movie != null) {
movie.close();
}
}
}
public boolean isUpdated(long traktId, String lastUpdated) {
Cursor movie = null;
try {
movie = resolver.query(Movies.MOVIES, new String[] {
MovieColumns.LAST_UPDATED,
}, MovieColumns.TRAKT_ID + "=?", new String[] {
String.valueOf(traktId),
}, null);
if (movie.moveToFirst()) {
return TimeUtils.getMillis(lastUpdated) > Cursors.getLong(movie, MovieColumns.LAST_UPDATED);
}
return false;
} finally {
if (movie != null) movie.close();
}
}
public boolean shouldUpdate(long traktId, String lastUpdated) {
if (lastUpdated == null) return true;
Cursor movie = null;
try {
movie = resolver.query(Movies.MOVIES, new String[] {
MovieColumns.WATCHED, MovieColumns.IN_COLLECTION, MovieColumns.IN_WATCHLIST,
}, MovieColumns.TRAKT_ID + "=?", new String[] {
String.valueOf(traktId),
}, null);
if (movie.moveToFirst()) {
final boolean watched = Cursors.getBoolean(movie, MovieColumns.WATCHED);
final boolean collected = Cursors.getBoolean(movie, MovieColumns.IN_COLLECTION);
final boolean inWatchlist = Cursors.getBoolean(movie, MovieColumns.IN_WATCHLIST);
if (watched || collected || inWatchlist) {
return true;
}
}
return false;
} finally {
if (movie != null) movie.close();
}
}
public static final class IdResult {
public long movieId;
public boolean didCreate;
public IdResult(long movieId, boolean didCreate) {
this.movieId = movieId;
this.didCreate = didCreate;
}
}
public IdResult getIdOrCreate(long traktId) {
synchronized (LOCK_ID) {
long id = getId(traktId);
if (id == -1L) {
id = create(traktId);
return new IdResult(id, true);
} else {
return new IdResult(id, false);
}
}
}
private long create(long traktId) {
ContentValues cv = new ContentValues();
cv.put(MovieColumns.TRAKT_ID, traktId);
cv.put(MovieColumns.NEEDS_SYNC, true);
return ProviderSchematic.Movies.getId(resolver.insert(Movies.MOVIES, cv));
}
public long fullUpdate(Movie movie) {
IdResult result = getIdOrCreate(movie.getIds().getTrakt());
final long id = result.movieId;
ContentValues cv = getContentValues(movie);
cv.put(MovieColumns.NEEDS_SYNC, false);
resolver.update(Movies.withId(id), cv, null, null);
if (movie.getGenres() != null) {
insertGenres(id, movie.getGenres());
}
return id;
}
public long partialUpdate(Movie movie) {
IdResult result = getIdOrCreate(movie.getIds().getTrakt());
final long id = result.movieId;
ContentValues cv = getContentValues(movie);
resolver.update(Movies.withId(id), cv, null, null);
if (movie.getGenres() != null) {
insertGenres(id, movie.getGenres());
}
return id;
}
public void insertGenres(long movieId, List<String> genres) {
try {
ArrayList<ContentProviderOperation> ops = new ArrayList<>();
ContentProviderOperation op;
op = ContentProviderOperation.newDelete(MovieGenres.fromMovie(movieId)).build();
ops.add(op);
for (String genre : genres) {
op = ContentProviderOperation.newInsert(MovieGenres.fromMovie(movieId))
.withValue(MovieGenreColumns.MOVIE_ID, movieId)
.withValue(MovieGenreColumns.GENRE, TextUtils.upperCaseFirstLetter(genre))
.build();
ops.add(op);
}
resolver.applyBatch(CathodeProvider.AUTHORITY, ops);
} catch (RemoteException e) {
Timber.e(e, "Updating movie genres failed");
} catch (OperationApplicationException e) {
Timber.e(e, "Updating movie genres failed");
}
}
private static ContentValues getContentValues(Movie movie) {
ContentValues cv = new ContentValues();
cv.put(MovieColumns.TITLE, movie.getTitle());
cv.put(MovieColumns.TITLE_NO_ARTICLE, DatabaseUtils.removeLeadingArticle(movie.getTitle()));
if (movie.getYear() != null) cv.put(MovieColumns.YEAR, movie.getYear());
if (movie.getReleased() != null) cv.put(MovieColumns.RELEASED, movie.getReleased());
if (movie.getRuntime() != null) cv.put(MovieColumns.RUNTIME, movie.getRuntime());
if (movie.getTagline() != null) cv.put(MovieColumns.TAGLINE, movie.getTagline());
if (movie.getOverview() != null) cv.put(MovieColumns.OVERVIEW, movie.getOverview());
cv.put(MovieColumns.TRAKT_ID, movie.getIds().getTrakt());
cv.put(MovieColumns.SLUG, movie.getIds().getSlug());
cv.put(MovieColumns.IMDB_ID, movie.getIds().getImdb());
cv.put(MovieColumns.TMDB_ID, movie.getIds().getTmdb());
if (movie.getLanguage() != null) cv.put(MovieColumns.LANGUAGE, movie.getLanguage());
if (movie.getCertification() != null) {
cv.put(MovieColumns.CERTIFICATION, movie.getCertification());
}
if (movie.getTrailer() != null) {
cv.put(MovieColumns.TRAILER, movie.getTrailer());
}
if (movie.getHomepage() != null) {
cv.put(MovieColumns.HOMEPAGE, movie.getHomepage());
}
if (movie.getRating() != null) {
cv.put(MovieColumns.RATING, movie.getRating());
}
if (movie.getVotes() != null) {
cv.put(MovieColumns.VOTES, movie.getVotes());
}
return cv;
}
public void setWatched(long movieId, boolean isWatched, long watchedAt) {
ContentValues cv = new ContentValues();
cv.put(MovieColumns.WATCHED, isWatched);
cv.put(MovieColumns.WATCHED_AT, watchedAt);
resolver.update(Movies.withId(movieId), cv, null, null);
}
public void setIsInCollection(long movieId, boolean collected) {
setIsInCollection(movieId, collected, 0);
}
public void setIsInCollection(long movieId, boolean collected, long collectedAt) {
ContentValues cv = new ContentValues();
cv.put(MovieColumns.IN_COLLECTION, collected);
cv.put(MovieColumns.COLLECTED_AT, collectedAt);
resolver.update(Movies.withId(movieId), cv, null, null);
}
public void setIsInWatchlist(long movieId, boolean inWatchlist) {
setIsInWatchlist(movieId, inWatchlist, 0);
}
public void setIsInWatchlist(long movieId, boolean inWatchlist, long listedAt) {
ContentValues cv = new ContentValues();
cv.put(MovieColumns.IN_WATCHLIST, inWatchlist);
cv.put(MovieColumns.LISTED_AT, listedAt);
resolver.update(Movies.withId(movieId), cv, null, null);
}
}