/* == This file is part of Tomahawk Player - <http://tomahawk-player.org> ===
*
* Copyright 2015, Enno Gottschalk <mrmaffen@googlemail.com>
*
* Tomahawk is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* Tomahawk is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with Tomahawk. If not, see <http://www.gnu.org/licenses/>.
*/
package org.tomahawk.libtomahawk.collection;
import org.jdeferred.Deferred;
import org.jdeferred.DoneCallback;
import org.jdeferred.Promise;
import org.tomahawk.libtomahawk.database.CollectionDb;
import org.tomahawk.libtomahawk.database.CollectionDbManager;
import org.tomahawk.libtomahawk.resolver.FuzzyIndex;
import org.tomahawk.libtomahawk.resolver.PipeLine;
import org.tomahawk.libtomahawk.resolver.Query;
import org.tomahawk.libtomahawk.resolver.Resolver;
import org.tomahawk.libtomahawk.resolver.Result;
import org.tomahawk.libtomahawk.resolver.ScriptAccount;
import org.tomahawk.libtomahawk.resolver.ScriptResolver;
import org.tomahawk.libtomahawk.utils.ADeferredObject;
import org.tomahawk.tomahawk_android.utils.ThreadManager;
import org.tomahawk.tomahawk_android.utils.TomahawkRunnable;
import android.database.Cursor;
import android.util.Log;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import de.greenrobot.event.EventBus;
/**
* This class represents a Collection which contains tracks/albums/artists which are being stored in
* a local sqlite db.
*/
public abstract class DbCollection extends Collection {
private final static String TAG = DbCollection.class.getSimpleName();
private Set<Query> mWaitingQueries = Collections
.newSetFromMap(new ConcurrentHashMap<Query, Boolean>());
private Resolver mResolver;
private boolean mInitialized;
public class InitializedEvent {
String collectionId;
}
public DbCollection(Resolver resolver) {
super(resolver.getId(), resolver.getPrettyName());
mResolver = resolver;
}
public boolean isInitialized() {
return mInitialized;
}
public void setInitialized(boolean initialized) {
mInitialized = initialized;
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
TomahawkRunnable r = new TomahawkRunnable(
TomahawkRunnable.PRIORITY_IS_DATABASEACTION) {
@Override
public void run() {
invokeWaitingJobs();
}
};
ThreadManager.get().execute(r);
InitializedEvent event = new InitializedEvent();
event.collectionId = collectionId;
EventBus.getDefault().post(event);
}
});
}
public String getIconBackgroundPath() {
if (mResolver instanceof ScriptResolver) {
ScriptAccount account = ((ScriptResolver) mResolver).getScriptAccount();
return account.getIconBackgroundPath();
}
return null;
}
public abstract Promise<String, Throwable, Void> getCollectionId();
public void resolve(final Query query) {
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
final CollectionDb db = CollectionDbManager.get().getCollectionDb(collectionId);
if (!mInitialized) {
mWaitingQueries.add(query);
Log.d(TAG, collectionId + " - Added query to the waiting queue because the "
+ "FuzzyIndex is still initializing.");
} else {
TomahawkRunnable r = new TomahawkRunnable(
TomahawkRunnable.PRIORITY_IS_RESOLVING) {
@Override
public void run() {
List<FuzzyIndex.IndexResult> indexResults =
db.getFuzzyIndex().searchIndex(query);
if (indexResults.size() > 0) {
String[] ids = new String[indexResults.size()];
for (int i = 0; i < indexResults.size(); i++) {
FuzzyIndex.IndexResult indexResult = indexResults.get(i);
ids[i] = String.valueOf(indexResult.id);
}
CollectionDb.WhereInfo whereInfo = new CollectionDb.WhereInfo();
whereInfo.connection = "OR";
whereInfo.where.put(CollectionDb.ID, ids);
Cursor cursor = db.tracks(whereInfo, null);
CollectionCursor<Result> collectionCursor = new CollectionCursor<>(
cursor, Result.class, mResolver, null);
ArrayList<Result> results = new ArrayList<>();
for (int i = 0; i < collectionCursor.size(); i++) {
results.add(collectionCursor.get(i));
}
collectionCursor.close();
PipeLine.get().reportResults(query, results, mResolver.getId());
}
}
};
ThreadManager.get().execute(r, query);
}
}
});
}
private synchronized void invokeWaitingJobs() {
Log.d(TAG, "Resolving " + mWaitingQueries.size() + " waiting queries");
for (Query query : mWaitingQueries) {
resolve(query);
}
mWaitingQueries.clear();
}
@Override
public Promise<Playlist, Throwable, Void> getQueries(final int sortMode) {
final Deferred<Playlist, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
String[] orderBy;
switch (sortMode) {
case SORT_ALPHA:
orderBy = new String[]{CollectionDb.TRACKS_TRACK};
break;
case SORT_ARTIST_ALPHA:
orderBy = new String[]{CollectionDb.ARTISTS_ARTIST};
break;
case SORT_LAST_MODIFIED:
orderBy = new String[]{CollectionDb.TRACKS_LASTMODIFIED + " DESC"};
break;
default:
Log.e(TAG,
collectionId + " - getQueries - sortMode not supported!");
return;
}
CollectionDb db = CollectionDbManager.get().getCollectionDb(collectionId);
String currentRevision = String.valueOf(db.tracksCurrentRevision());
Playlist playlist = Playlist.get(
collectionId + "_tracks_" + currentRevision + "_" + sortMode);
if (playlist.getCurrentRevision().isEmpty()) {
Cursor cursor = db.tracks(null, orderBy);
if (cursor == null) {
deferred.resolve(null);
return;
}
CollectionCursor<PlaylistEntry> collectionCursor
= new CollectionCursor<>(
cursor, PlaylistEntry.class, mResolver, playlist);
playlist.setCursor(collectionCursor);
playlist.setFilled(true);
playlist.setCurrentRevision(currentRevision);
}
deferred.resolve(playlist);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<CollectionCursor<Artist>, Throwable, Void> getArtists(final int sortMode) {
final Deferred<CollectionCursor<Artist>, Throwable, Void> deferred
= new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
String[] orderBy;
switch (sortMode) {
case SORT_ALPHA:
orderBy = new String[]{
CollectionDb.ARTISTS_ARTIST + " COLLATE NOCASE "};
break;
case SORT_LAST_MODIFIED:
orderBy = new String[]{CollectionDb.ARTISTS_LASTMODIFIED + " DESC"};
break;
default:
Log.e(TAG,
collectionId + " - getArtists - sortMode not supported!");
return;
}
Cursor cursor = CollectionDbManager.get().getCollectionDb(collectionId)
.artists(orderBy);
CollectionCursor<Artist> collectionCursor =
new CollectionCursor<>(cursor, Artist.class, null, null);
deferred.resolve(collectionCursor);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<CollectionCursor<Artist>, Throwable, Void> getAlbumArtists(final int sortMode) {
final Deferred<CollectionCursor<Artist>, Throwable, Void> deferred
= new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
String[] orderBy;
switch (sortMode) {
case SORT_ALPHA:
orderBy = new String[]{
CollectionDb.ARTISTS_ARTIST + " COLLATE NOCASE "};
break;
case SORT_LAST_MODIFIED:
orderBy = new String[]{CollectionDb.ARTISTS_LASTMODIFIED + " DESC"};
break;
default:
Log.e(TAG, collectionId
+ " - getAlbumArtists - sortMode not supported!");
return;
}
Cursor cursor = CollectionDbManager.get().getCollectionDb(collectionId)
.albumArtists(orderBy);
CollectionCursor<Artist> collectionCursor =
new CollectionCursor<>(cursor, Artist.class, null, null);
deferred.resolve(collectionCursor);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<CollectionCursor<Album>, Throwable, Void> getAlbums(final int sortMode) {
final Deferred<CollectionCursor<Album>, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
String[] orderBy;
switch (sortMode) {
case SORT_ALPHA:
orderBy = new String[]{
CollectionDb.ALBUMS_ALBUM + " COLLATE NOCASE "};
break;
case SORT_ARTIST_ALPHA:
orderBy = new String[]{
CollectionDb.ARTISTS_ARTIST + " COLLATE NOCASE "};
break;
case SORT_LAST_MODIFIED:
orderBy = new String[]{CollectionDb.ALBUMS_LASTMODIFIED + " DESC"};
break;
default:
Log.e(TAG, collectionId + " - getAlbums - sortMode not supported!");
return;
}
Cursor cursor = CollectionDbManager.get().getCollectionDb(collectionId)
.albums(orderBy);
CollectionCursor<Album> collectionCursor =
new CollectionCursor<>(cursor, Album.class, null, null);
deferred.resolve(collectionCursor);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<CollectionCursor<Album>, Throwable, Void> getArtistAlbums(final Artist artist) {
final Deferred<CollectionCursor<Album>, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String result) {
new Thread(new Runnable() {
@Override
public void run() {
Cursor cursor = CollectionDbManager.get().getCollectionDb(result)
.artistAlbums(artist.getName(), "");
if (cursor == null) {
deferred.resolve(null);
return;
}
CollectionCursor<Album> collectionCursor =
new CollectionCursor<>(cursor, Album.class, null, null);
deferred.resolve(collectionCursor);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<Playlist, Throwable, Void> getArtistTracks(final Artist artist) {
final Deferred<Playlist, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
CollectionDb db = CollectionDbManager.get().getCollectionDb(collectionId);
String currentRevision =
String.valueOf(db.artistCurrentRevision(artist.getName(), ""));
Playlist playlist = Playlist.get(
collectionId + "_" + artist.getCacheKey() + "_" + currentRevision);
if (playlist.getCurrentRevision().isEmpty()) {
Cursor cursor = db.artistTracks(artist.getName(), "");
if (cursor == null) {
deferred.resolve(null);
return;
}
CollectionCursor<PlaylistEntry> collectionCursor
= new CollectionCursor<>(
cursor, PlaylistEntry.class, mResolver, playlist);
playlist.setCursor(collectionCursor);
playlist.setFilled(true);
playlist.setCurrentRevision(currentRevision);
}
deferred.resolve(playlist);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<Playlist, Throwable, Void> getAlbumTracks(final Album album) {
final Deferred<Playlist, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
CollectionDb db = CollectionDbManager.get().getCollectionDb(collectionId);
String currentRevision = String.valueOf(db.albumCurrentRevision(
album.getName(), album.getArtist().getName(), ""));
Playlist playlist = Playlist.get(
collectionId + "_" + album.getCacheKey() + "_" + currentRevision);
if (playlist.getCurrentRevision().isEmpty()) {
Cursor cursor = db.albumTracks(
album.getName(), album.getArtist().getName(), "");
if (cursor == null) {
deferred.resolve(null);
return;
}
CollectionCursor<PlaylistEntry> collectionCursor
= new CollectionCursor<>(
cursor, PlaylistEntry.class, mResolver, playlist);
playlist.setCursor(collectionCursor);
playlist.setFilled(true);
playlist.setCurrentRevision(currentRevision);
}
deferred.resolve(playlist);
}
}).start();
}
});
return deferred;
}
@Override
public Promise<Integer, Throwable, Void> getAlbumTrackCount(final Album album) {
final Deferred<Integer, Throwable, Void> deferred = new ADeferredObject<>();
getCollectionId().done(new DoneCallback<String>() {
@Override
public void onDone(final String collectionId) {
new Thread(new Runnable() {
@Override
public void run() {
Cursor cursor = CollectionDbManager.get().getCollectionDb(collectionId)
.albumTracks(album.getName(), album.getArtist().getName(), "");
if (cursor == null) {
deferred.resolve(null);
return;
}
deferred.resolve(cursor.getCount());
cursor.close();
}
}).start();
}
});
return deferred;
}
}