move observer notifications into MediaLibrary from MediaLibraryBackend

This commit is contained in:
Adrian Ulrich 2016-11-28 20:03:47 +01:00
parent fd7d3cb31b
commit 3d49310a9d
4 changed files with 99 additions and 73 deletions

View File

@ -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

View File

@ -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;
}

View File

@ -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;
}
}

View File

@ -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();