From eba6acabc2e9a4215c8053d32f5f700f3a782eda Mon Sep 17 00:00:00 2001 From: Adrian Ulrich Date: Sat, 30 Jun 2018 10:32:21 +0200 Subject: [PATCH] introduce PlaylistObserver --- .../android/medialibrary/LibraryObserver.java | 8 +- .../android/medialibrary/MediaLibrary.java | 27 ++++--- .../android/medialibrary/MediaScanner.java | 6 +- .../android/vanilla/PlaybackService.java | 9 ++- .../android/vanilla/PlaylistObserver.java | 80 +++++++++++++++++++ .../vanilla/ScheduledLibraryUpdate.java | 2 +- 6 files changed, 114 insertions(+), 18 deletions(-) create mode 100644 app/src/main/java/ch/blinkenlights/android/vanilla/PlaylistObserver.java diff --git a/app/src/main/java/ch/blinkenlights/android/medialibrary/LibraryObserver.java b/app/src/main/java/ch/blinkenlights/android/medialibrary/LibraryObserver.java index ee70714e..e612c24c 100644 --- a/app/src/main/java/ch/blinkenlights/android/medialibrary/LibraryObserver.java +++ b/app/src/main/java/ch/blinkenlights/android/medialibrary/LibraryObserver.java @@ -26,9 +26,8 @@ public class LibraryObserver { * by the receiver */ public enum Type { - ANY, // Any type may have changed - SONG, // Change only affected song entries - PLAYLIST, // Change only affected playlists + SONG, // Change affected song entries + PLAYLIST, // Change affected playlists } /** @@ -42,9 +41,10 @@ public class LibraryObserver { * to be overriden by the registered observer. * * @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. */ - public void onChange(Type type, boolean ongoing) { + public void onChange(Type type, long id, boolean ongoing) { // NOOP, should be overriden. } } diff --git a/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaLibrary.java b/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaLibrary.java index bf13a5be..9440d581 100644 --- a/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaLibrary.java +++ b/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaLibrary.java @@ -324,11 +324,15 @@ public class MediaLibrary { /** * 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 list = sLibraryObservers; 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) { getBackend(context).cleanOrphanedEntries(true); - notifyObserver(LibraryObserver.Type.ANY, false); + notifyObserver(LibraryObserver.Type.SONG, id, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, -1, false); } return rows; } @@ -389,7 +394,7 @@ public class MediaLibrary { long id = getBackend(context).insert(MediaLibrary.TABLE_PLAYLISTS, null, v); if (id != -1) - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, id, false); return id; } @@ -407,7 +412,7 @@ public class MediaLibrary { boolean removed = (rows > 0); if (removed) - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, id, false); return removed; } @@ -445,7 +450,7 @@ public class MediaLibrary { int rows = getBackend(context).bulkInsert(MediaLibrary.TABLE_PLAYLISTS_SONGS, null, bulk); if (rows > 0) - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false); return rows; } @@ -461,7 +466,7 @@ public class MediaLibrary { int rows = getBackend(context).delete(MediaLibrary.TABLE_PLAYLISTS_SONGS, selection, selectionArgs); if (rows > 0) - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, -1, false); return rows; } @@ -483,8 +488,10 @@ public class MediaLibrary { removePlaylist(context, playlistId); } - if (newId != -1) - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + if (newId != -1) { + notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, newId, false); + } return newId; } @@ -529,7 +536,7 @@ public class MediaLibrary { selection = MediaLibrary.PlaylistSongColumns._ID+"="+from; getBackend(context).update(MediaLibrary.TABLE_PLAYLISTS_SONGS, v, selection, null); - notifyObserver(LibraryObserver.Type.PLAYLIST, false); + notifyObserver(LibraryObserver.Type.PLAYLIST, playlistId, false); } /** diff --git a/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaScanner.java b/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaScanner.java index 5fbaa376..4ed8bba7 100644 --- a/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaScanner.java +++ b/app/src/main/java/ch/blinkenlights/android/medialibrary/MediaScanner.java @@ -205,7 +205,7 @@ public class MediaScanner implements Handler.Callback { switch (rpc) { case MSG_NOTIFY_CHANGE: { - MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, true); + MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, -1, true); break; } case MSG_SCAN_FINISHED: { @@ -216,6 +216,8 @@ public class MediaScanner implements Handler.Callback { if (mPendingCleanup) { mPendingCleanup = false; 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. @@ -224,7 +226,7 @@ public class MediaScanner implements Handler.Callback { // also signals that this will be our last update // for this scan. mHandler.removeMessages(MSG_NOTIFY_CHANGE); - MediaLibrary.notifyObserver(LibraryObserver.Type.ANY, false); + MediaLibrary.notifyObserver(LibraryObserver.Type.SONG, -1, false); updateNotification(false); break; diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java index 61258c36..6de422eb 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaybackService.java @@ -431,6 +431,10 @@ public final class PlaybackService extends Service * Reference to precreated ReadAhead thread */ private ReadaheadThread mReadahead; + /** + * Referente to our playlist observer + */ + private PlaylistObserver mPlaylistObserver; /** * Reference to precreated BASTP Object */ @@ -502,6 +506,8 @@ public final class PlaybackService extends Service mRemoteControlClient = new RemoteControl().getClient(this); mRemoteControlClient.initializeRemote(); + mPlaylistObserver = new PlaylistObserver(); + mLooper = thread.getLooper(); mHandler = new Handler(mLooper, this); @@ -616,6 +622,7 @@ public final class PlaybackService extends Service enterSleepState(); MediaLibrary.unregisterLibraryObserver(mObserver); + mPlaylistObserver.unregister(); if (mMediaPlayer != null) { mMediaPlayer.release(); @@ -1932,7 +1939,7 @@ public final class PlaybackService extends Service private final LibraryObserver mObserver = new LibraryObserver() { @Override - public void onChange(LibraryObserver.Type type, boolean ongoing) + public void onChange(LibraryObserver.Type type, long id, boolean ongoing) { MediaUtils.onMediaChange(); onMediaChange(); diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/PlaylistObserver.java b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaylistObserver.java new file mode 100644 index 00000000..74419da4 --- /dev/null +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/PlaylistObserver.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2018 Adrian Ulrich + * + * 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 . + */ + +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? +} diff --git a/app/src/main/java/ch/blinkenlights/android/vanilla/ScheduledLibraryUpdate.java b/app/src/main/java/ch/blinkenlights/android/vanilla/ScheduledLibraryUpdate.java index 79e6a78a..79bf6cf0 100644 --- a/app/src/main/java/ch/blinkenlights/android/vanilla/ScheduledLibraryUpdate.java +++ b/app/src/main/java/ch/blinkenlights/android/vanilla/ScheduledLibraryUpdate.java @@ -133,7 +133,7 @@ public class ScheduledLibraryUpdate extends JobService { */ private final LibraryObserver mObserver = new LibraryObserver() { @Override - public void onChange(LibraryObserver.Type type, boolean ongoing) { + public void onChange(LibraryObserver.Type type, long id, boolean ongoing) { if (!ongoing) { jobFinished(mJobParams, false); finalizeScan();