introduce PlaylistObserver

This commit is contained in:
Adrian Ulrich 2018-06-30 10:32:21 +02:00
parent d8b276d586
commit eba6acabc2
6 changed files with 114 additions and 18 deletions

View File

@ -26,9 +26,8 @@ public class LibraryObserver {
* by the receiver * by the receiver
*/ */
public enum Type { public enum Type {
ANY, // Any type may have changed SONG, // Change affected song entries
SONG, // Change only affected song entries PLAYLIST, // Change affected playlists
PLAYLIST, // Change only affected playlists
} }
/** /**
@ -42,9 +41,10 @@ public class LibraryObserver {
* to be overriden by the registered observer. * to be overriden by the registered observer.
* *
* @param type one of LibraryObserver.Type * @param type one of LibraryObserver.Type
* @param id hint of which id changed for type, -1 if unspecified.
* @param ongoing whether or not to expect more events soon. * @param ongoing whether or not to expect more events soon.
*/ */
public void onChange(Type type, boolean ongoing) { public void onChange(Type type, long id, boolean ongoing) {
// NOOP, should be overriden. // NOOP, should be overriden.
} }
} }

View File

@ -324,11 +324,15 @@ public class MediaLibrary {
/** /**
* Broadcasts a change to all registered observers * Broadcasts a change to all registered observers
*
* @param type the type of this event.
* @param id the id of type which changed, -1 if unknown
* @param ongoing whether or not to expect more of these updates soon
*/ */
static void notifyObserver(LibraryObserver.Type type, boolean ongoing) { static void notifyObserver(LibraryObserver.Type type, long id, boolean ongoing) {
ArrayList<LibraryObserver> list = sLibraryObservers; ArrayList<LibraryObserver> list = sLibraryObservers;
for (int i = list.size(); --i != -1; ) for (int i = list.size(); --i != -1; )
list.get(i).onChange(type, ongoing); list.get(i).onChange(type, id, ongoing);
} }
/** /**
@ -357,7 +361,8 @@ public class MediaLibrary {
if (rows > 0) { if (rows > 0) {
getBackend(context).cleanOrphanedEntries(true); getBackend(context).cleanOrphanedEntries(true);
notifyObserver(LibraryObserver.Type.ANY, false); notifyObserver(LibraryObserver.Type.SONG, id, false);
notifyObserver(LibraryObserver.Type.PLAYLIST, -1, false);
} }
return rows; return rows;
} }
@ -389,7 +394,7 @@ public class MediaLibrary {
long id = getBackend(context).insert(MediaLibrary.TABLE_PLAYLISTS, null, v); long id = getBackend(context).insert(MediaLibrary.TABLE_PLAYLISTS, null, v);
if (id != -1) if (id != -1)
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, id, false);
return id; return id;
} }
@ -407,7 +412,7 @@ public class MediaLibrary {
boolean removed = (rows > 0); boolean removed = (rows > 0);
if (removed) if (removed)
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, id, false);
return removed; return removed;
} }
@ -445,7 +450,7 @@ public class MediaLibrary {
int rows = getBackend(context).bulkInsert(MediaLibrary.TABLE_PLAYLISTS_SONGS, null, bulk); int rows = getBackend(context).bulkInsert(MediaLibrary.TABLE_PLAYLISTS_SONGS, null, bulk);
if (rows > 0) if (rows > 0)
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false);
return rows; return rows;
} }
@ -461,7 +466,7 @@ public class MediaLibrary {
int rows = getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS_SONGS, selection, selectionArgs); int rows = getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS_SONGS, selection, selectionArgs);
if (rows > 0) if (rows > 0)
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, -1, false);
return rows; return rows;
} }
@ -483,8 +488,10 @@ public class MediaLibrary {
removePlaylist(context, playlistId); removePlaylist(context, playlistId);
} }
if (newId != -1) if (newId != -1) {
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false);
notifyObserver(LibraryObserver.Type.PLAYLIST, newId, false);
}
return newId; return newId;
} }
@ -529,7 +536,7 @@ public class MediaLibrary {
selection = MediaLibrary.PlaylistSongColumns._ID+"="+from; selection = MediaLibrary.PlaylistSongColumns._ID+"="+from;
getBackend(context).update(MediaLibrary.TABLE_PLAYLISTS_SONGS, v, selection, null); getBackend(context).update(MediaLibrary.TABLE_PLAYLISTS_SONGS, v, selection, null);
notifyObserver(LibraryObserver.Type.PLAYLIST, false); notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false);
} }
/** /**

View File

@ -205,7 +205,7 @@ public class MediaScanner implements Handler.Callback {
switch (rpc) { switch (rpc) {
case MSG_NOTIFY_CHANGE: { case MSG_NOTIFY_CHANGE: {
MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, true); MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, -1, true);
break; break;
} }
case MSG_SCAN_FINISHED: { case MSG_SCAN_FINISHED: {
@ -216,6 +216,8 @@ public class MediaScanner implements Handler.Callback {
if (mPendingCleanup) { if (mPendingCleanup) {
mPendingCleanup = false; mPendingCleanup = false;
mBackend.cleanOrphanedEntries(true); mBackend.cleanOrphanedEntries(true);
// scan run possibly deleted file which may affect playlists:
MediaLibrary.notifyObserver(LibraryObserver.Type.PLAYLIST, -1, false);
} }
// Send a last change notification to all observers. // Send a last change notification to all observers.
@ -224,7 +226,7 @@ public class MediaScanner implements Handler.Callback {
// also signals that this will be our last update // also signals that this will be our last update
// for this scan. // for this scan.
mHandler.removeMessages(MSG_NOTIFY_CHANGE); mHandler.removeMessages(MSG_NOTIFY_CHANGE);
MediaLibrary.notifyObserver(LibraryObserver.Type.ANY, false); MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, -1, false);
updateNotification(false); updateNotification(false);
break; break;

View File

@ -431,6 +431,10 @@ public final class PlaybackService extends Service
* Reference to precreated ReadAhead thread * Reference to precreated ReadAhead thread
*/ */
private ReadaheadThread mReadahead; private ReadaheadThread mReadahead;
/**
* Referente to our playlist observer
*/
private PlaylistObserver mPlaylistObserver;
/** /**
* Reference to precreated BASTP Object * Reference to precreated BASTP Object
*/ */
@ -502,6 +506,8 @@ public final class PlaybackService extends Service
mRemoteControlClient = new RemoteControl().getClient(this); mRemoteControlClient = new RemoteControl().getClient(this);
mRemoteControlClient.initializeRemote(); mRemoteControlClient.initializeRemote();
mPlaylistObserver = new PlaylistObserver();
mLooper = thread.getLooper(); mLooper = thread.getLooper();
mHandler = new Handler(mLooper, this); mHandler = new Handler(mLooper, this);
@ -616,6 +622,7 @@ public final class PlaybackService extends Service
enterSleepState(); enterSleepState();
MediaLibrary.unregisterLibraryObserver(mObserver); MediaLibrary.unregisterLibraryObserver(mObserver);
mPlaylistObserver.unregister();
if (mMediaPlayer != null) { if (mMediaPlayer != null) {
mMediaPlayer.release(); mMediaPlayer.release();
@ -1932,7 +1939,7 @@ public final class PlaybackService extends Service
private final LibraryObserver mObserver = new LibraryObserver() { private final LibraryObserver mObserver = new LibraryObserver() {
@Override @Override
public void onChange(LibraryObserver.Type type, boolean ongoing) public void onChange(LibraryObserver.Type type, long id, boolean ongoing)
{ {
MediaUtils.onMediaChange(); MediaUtils.onMediaChange();
onMediaChange(); onMediaChange();

View File

@ -0,0 +1,80 @@
/*
* Copyright (C) 2018 Adrian Ulrich <adrian@blinkenlights.ch>
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package ch.blinkenlights.android.vanilla;
import ch.blinkenlights.android.medialibrary.MediaLibrary;
import ch.blinkenlights.android.medialibrary.LibraryObserver;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.os.Process;
import java.io.File;
import android.util.Log;
public class PlaylistObserver implements Handler.Callback {
/**
* Handler thread used to perform playlist sync.
*/
private Handler mHandler;
/**
* Directory which holds observed playlists.
*/
private File mPlaylists = new File(Environment.getExternalStorageDirectory(), "Playlists");
public PlaylistObserver() {
// Create thread which will be used for background work.
HandlerThread handlerThread = new HandlerThread("PlaylistWriter", Process.THREAD_PRIORITY_LOWEST);
handlerThread.start();
mHandler = new Handler(handlerThread.getLooper(), this);
// Register to receive media library events.
MediaLibrary.registerLibraryObserver(mObserver);
}
/**
* Unregisters this observer, the object must not be used anymore
* after this function was called.
*/
public void unregister() {
MediaLibrary.unregisterLibraryObserver(mObserver);
}
@Override
public boolean handleMessage(Message message) {
return true;
}
/**
* Library observer callback which notifies us about media library
* events.
*/
private final LibraryObserver mObserver = new LibraryObserver() {
@Override
public void onChange(LibraryObserver.Type type, long id, boolean ongoing) {
Log.v("VanillaMusic", "onChange type = "+type+", id = "+id+" ongoing = "+ongoing);
}
};
// TODO:
// Use FileObserver to track playlist changes?
// how do we check modifications? write a shadow-dir in private app storage with same mtimes?
}

View File

@ -133,7 +133,7 @@ public class ScheduledLibraryUpdate extends JobService {
*/ */
private final LibraryObserver mObserver = new LibraryObserver() { private final LibraryObserver mObserver = new LibraryObserver() {
@Override @Override
public void onChange(LibraryObserver.Type type, boolean ongoing) { public void onChange(LibraryObserver.Type type, long id, boolean ongoing) {
if (!ongoing) { if (!ongoing) {
jobFinished(mJobParams, false); jobFinished(mJobParams, false);
finalizeScan(); finalizeScan();