move observer notifications into MediaLibrary from MediaLibraryBackend
This commit is contained in:
parent
fd7d3cb31b
commit
3d49310a9d
@ -45,9 +45,18 @@ public class MediaLibrary {
|
||||
public static final int ROLE_ARTIST = 0;
|
||||
public static final int ROLE_COMPOSER = 1;
|
||||
|
||||
/**
|
||||
* Our static backend instance
|
||||
*/
|
||||
private static MediaLibraryBackend sBackend;
|
||||
|
||||
/**
|
||||
* An instance to the created scanner thread during our own creation
|
||||
*/
|
||||
private static MediaScanner sScanner;
|
||||
/**
|
||||
* The observer to call-back during database changes
|
||||
*/
|
||||
private static ContentObserver sContentObserver;
|
||||
|
||||
private static MediaLibraryBackend getBackend(Context context) {
|
||||
if (sBackend == null) {
|
||||
@ -66,6 +75,28 @@ public class MediaLibrary {
|
||||
return sBackend;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new content observer for the media library
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param observer the content observer we are going to call on changes
|
||||
*/
|
||||
public static void registerContentObserver(ContentObserver observer) {
|
||||
if (sContentObserver == null) {
|
||||
sContentObserver = observer;
|
||||
} else {
|
||||
throw new IllegalStateException("ContentObserver was already registered");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcasts a change to the observer, which will queue and dispatch
|
||||
* the event to any registered observer
|
||||
*/
|
||||
static void notifyObserver() {
|
||||
if (sContentObserver != null)
|
||||
sContentObserver.onChange(true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Perform a media query on the database, returns a cursor
|
||||
@ -86,9 +117,14 @@ public class MediaLibrary {
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param id the song id to delete
|
||||
* @return the number of affected rows
|
||||
*/
|
||||
public static void removeSong(Context context, long id) {
|
||||
getBackend(context).delete(TABLE_SONGS, SongColumns._ID+"="+id, null);
|
||||
public static int removeSong(Context context, long id) {
|
||||
int rows = getBackend(context).delete(TABLE_SONGS, SongColumns._ID+"="+id, null);
|
||||
|
||||
if (rows > 0)
|
||||
notifyObserver();
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -115,7 +151,11 @@ public class MediaLibrary {
|
||||
ContentValues v = new ContentValues();
|
||||
v.put(MediaLibrary.PlaylistColumns._ID, hash63(name));
|
||||
v.put(MediaLibrary.PlaylistColumns.NAME, name);
|
||||
return getBackend(context).insert(MediaLibrary.TABLE_PLAYLISTS, null, v);
|
||||
long id = getBackend(context).insert(MediaLibrary.TABLE_PLAYLISTS, null, v);
|
||||
|
||||
if (id != -1)
|
||||
notifyObserver();
|
||||
return id;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -129,7 +169,11 @@ public class MediaLibrary {
|
||||
// first, wipe all songs
|
||||
removeFromPlaylist(context, MediaLibrary.PlaylistSongColumns.PLAYLIST_ID+"="+id, null);
|
||||
int rows = getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS, MediaLibrary.PlaylistColumns._ID+"="+id, null);
|
||||
return (rows > 0);
|
||||
boolean removed = (rows > 0);
|
||||
|
||||
if (removed)
|
||||
notifyObserver();
|
||||
return removed;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -163,7 +207,11 @@ public class MediaLibrary {
|
||||
bulk.add(v);
|
||||
pos++;
|
||||
}
|
||||
return getBackend(context).bulkInsert(MediaLibrary.TABLE_PLAYLISTS_SONGS, null, bulk);
|
||||
int rows = getBackend(context).bulkInsert(MediaLibrary.TABLE_PLAYLISTS_SONGS, null, bulk);
|
||||
|
||||
if (rows > 0)
|
||||
notifyObserver();
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -175,7 +223,11 @@ public class MediaLibrary {
|
||||
* @return the number of deleted rows, -1 on error
|
||||
*/
|
||||
public static int removeFromPlaylist(Context context, String selection, String[] selectionArgs) {
|
||||
return getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS_SONGS, selection, selectionArgs);
|
||||
int rows = getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS_SONGS, selection, selectionArgs);
|
||||
|
||||
if (rows > 0)
|
||||
notifyObserver();
|
||||
return rows;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -195,6 +247,9 @@ public class MediaLibrary {
|
||||
getBackend(context).update(MediaLibrary.TABLE_PLAYLISTS_SONGS, v, selection, null);
|
||||
removePlaylist(context, playlistId);
|
||||
}
|
||||
|
||||
if (newId != -1)
|
||||
notifyObserver();
|
||||
return newId;
|
||||
}
|
||||
|
||||
@ -238,25 +293,10 @@ public class MediaLibrary {
|
||||
v.put(MediaLibrary.PlaylistSongColumns.POSITION, toPos);
|
||||
selection = MediaLibrary.PlaylistSongColumns._ID+"="+from;
|
||||
getBackend(context).update(MediaLibrary.TABLE_PLAYLISTS_SONGS, v, selection, null);
|
||||
|
||||
notifyObserver();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new content observer for the media library
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param observer the content observer we are going to call on changes
|
||||
*/
|
||||
public static void registerContentObserver(Context context, ContentObserver observer) {
|
||||
getBackend(context).registerContentObserver(observer);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if we are currently scanning for media
|
||||
*/
|
||||
public static boolean isScannerRunning(Context context) {
|
||||
// FIXME: IMPLEMENT THIS
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the 'key' of given string used for sorting and searching
|
||||
|
@ -19,7 +19,6 @@ package ch.blinkenlights.android.medialibrary;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContentValues;
|
||||
import android.database.ContentObserver;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
@ -54,10 +53,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
* Regexp to detect costy artist_id queries which we can optimize
|
||||
*/
|
||||
private static final Pattern sQueryMatchArtistSearch = Pattern.compile("(^|.+ )"+MediaLibrary.ContributorColumns.ARTIST_ID+"=(\\d+)$");
|
||||
/**
|
||||
* A list of registered content observers
|
||||
*/
|
||||
private ContentObserver mContentObserver;
|
||||
|
||||
/**
|
||||
* Constructor for the MediaLibraryBackend helper
|
||||
@ -101,28 +96,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
return count != 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a new observer which we call on database changes
|
||||
*
|
||||
* @param observer the observer to register
|
||||
*/
|
||||
public void registerContentObserver(ContentObserver observer) {
|
||||
if (mContentObserver == null) {
|
||||
mContentObserver = observer;
|
||||
} else {
|
||||
throw new IllegalStateException("ContentObserver was already registered");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends a callback to the registered observer
|
||||
*/
|
||||
private void notifyObserver() {
|
||||
if (mContentObserver != null)
|
||||
mContentObserver.onChange(true);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Wrapper for SQLiteDatabse.delete() function
|
||||
*
|
||||
@ -136,7 +109,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
int res = dbh.delete(table, whereClause, whereArgs);
|
||||
if (res > 0) {
|
||||
cleanOrphanedEntries();
|
||||
notifyObserver();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
@ -153,11 +125,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
public int update (String table, ContentValues values, String whereClause, String[] whereArgs) {
|
||||
SQLiteDatabase dbh = getWritableDatabase();
|
||||
int res = dbh.update(table, values, whereClause, whereArgs);
|
||||
if (res > 0) {
|
||||
// Note: we are not running notifyObserver for performance reasons here
|
||||
// Code which changes relations should just delete + re-insert data
|
||||
notifyObserver();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -185,8 +152,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
// avoid logspam as done by insert()
|
||||
}
|
||||
|
||||
if (result != -1)
|
||||
notifyObserver();
|
||||
return result;
|
||||
}
|
||||
|
||||
@ -219,9 +184,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
|
||||
dbh.endTransaction();
|
||||
}
|
||||
|
||||
if (count > 0)
|
||||
notifyObserver();
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
import android.os.SystemClock;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
@ -32,6 +33,14 @@ import java.util.HashMap;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class MediaScanner implements Handler.Callback {
|
||||
/**
|
||||
* How long to wait until we post an update notification
|
||||
*/
|
||||
private final static int SCAN_NOTIFY_DELAY_MS = 1200;
|
||||
/**
|
||||
* At which (up-)time we shall trigger the next notification
|
||||
*/
|
||||
private long mNextNotification = 0;
|
||||
/**
|
||||
* The backend instance we are acting on
|
||||
*/
|
||||
@ -66,7 +75,8 @@ public class MediaScanner implements Handler.Callback {
|
||||
}
|
||||
|
||||
private static final int MSG_SCAN_DIRECTORY = 1;
|
||||
private static final int MSG_SCAN_FILE = 2;
|
||||
private static final int MSG_ADD_FILE = 2;
|
||||
|
||||
@Override
|
||||
public boolean handleMessage(Message message) {
|
||||
File file = (File)message.obj;
|
||||
@ -76,14 +86,27 @@ public class MediaScanner implements Handler.Callback {
|
||||
scanDirectory(file, recursive);
|
||||
break;
|
||||
}
|
||||
case MSG_SCAN_FILE: {
|
||||
scanFile(file);
|
||||
case MSG_ADD_FILE: {
|
||||
long now = SystemClock.uptimeMillis();
|
||||
boolean changed = addFile(file);
|
||||
|
||||
// Notify the observer if this was the last message OR if the deadline was reached
|
||||
if (mHandler.hasMessages(MSG_ADD_FILE) == false || (mNextNotification != 0 && now >= mNextNotification)) {
|
||||
MediaLibrary.notifyObserver();
|
||||
mNextNotification = 0;
|
||||
}
|
||||
|
||||
// Initiate a new notification trigger if the old one fired and we got a change
|
||||
if (changed && mNextNotification == 0)
|
||||
mNextNotification = now + SCAN_NOTIFY_DELAY_MS;
|
||||
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
throw new IllegalArgumentException();
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -106,7 +129,7 @@ public class MediaScanner implements Handler.Callback {
|
||||
|
||||
for (File file : dirents) {
|
||||
if (file.isFile()) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_FILE, 0, 0, file));
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_ADD_FILE, 0, 0, file));
|
||||
} else if (file.isDirectory() && recursive) {
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_DIRECTORY, 1, 0, file));
|
||||
}
|
||||
@ -126,23 +149,24 @@ public class MediaScanner implements Handler.Callback {
|
||||
/**
|
||||
* Scans a single file and adds it to the database
|
||||
*
|
||||
* @param file the file to scan
|
||||
* @param file the file to add
|
||||
* @return true if we modified the database
|
||||
*/
|
||||
private void scanFile(File file) {
|
||||
private boolean addFile(File file) {
|
||||
String path = file.getAbsolutePath();
|
||||
long songId = MediaLibrary.hash63(path);
|
||||
|
||||
if (isBlacklisted(file))
|
||||
return;
|
||||
return false;
|
||||
|
||||
if (mBackend.isSongExisting(songId)) {
|
||||
Log.v("VanillaMusic", "Skipping already known song with id "+songId);
|
||||
return;
|
||||
return false;
|
||||
}
|
||||
|
||||
MediaMetadataExtractor tags = new MediaMetadataExtractor(path);
|
||||
if (tags.isEmpty())
|
||||
return; // file does not contain audio data
|
||||
return false; // file does not contain audio data
|
||||
|
||||
// Get tags which always must be set
|
||||
String title = tags.getFirst(MediaMetadataExtractor.TITLE);
|
||||
@ -227,8 +251,8 @@ public class MediaScanner implements Handler.Callback {
|
||||
mBackend.insert(MediaLibrary.TABLE_GENRES_SONGS, null, v);
|
||||
}
|
||||
}
|
||||
|
||||
Log.v("VanillaMusic", "MediaScanner: inserted "+path);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -483,7 +483,7 @@ public final class PlaybackService extends Service
|
||||
filter.addAction(Intent.ACTION_SCREEN_ON);
|
||||
registerReceiver(mReceiver, filter);
|
||||
|
||||
MediaLibrary.registerContentObserver(getApplicationContext(), mObserver);
|
||||
MediaLibrary.registerContentObserver(mObserver);
|
||||
|
||||
mRemoteControlClient = new RemoteControl().getClient(this);
|
||||
mRemoteControlClient.initializeRemote();
|
||||
|
Loading…
x
Reference in New Issue
Block a user