Make playlist folder a config option

This commit is contained in:
Adrian Ulrich 2018-07-15 21:01:41 +02:00
parent a8f84d85b4
commit 4e3c440f80
11 changed files with 163 additions and 51 deletions

View File

@ -172,6 +172,8 @@ THE SOFTWARE.
android:name="TabOrderActivity" />
<activity
android:name="FilebrowserStartActivity" />
<activity
android:name="PlaylistObserverDirActivity" />
<activity
android:name="MediaFoldersSelectionActivity" />
<activity

View File

@ -507,7 +507,8 @@ public final class PlaybackService extends Service
mRemoteControlClient.initializeRemote();
int syncMode = Integer.parseInt(settings.getString(PrefKeys.PLAYLIST_SYNC_MODE, PrefDefaults.PLAYLIST_SYNC_MODE));
mPlaylistObserver = new PlaylistObserver(this, syncMode);
String syncFolder = settings.getString(PrefKeys.PLAYLIST_SYNC_FOLDER, PrefDefaults.PLAYLIST_SYNC_FOLDER);
mPlaylistObserver = new PlaylistObserver(this, syncFolder, syncMode);
mLooper = thread.getLooper();
mHandler = new Handler(mLooper, this);
@ -941,9 +942,12 @@ public final class PlaybackService extends Service
mReadaheadEnabled = settings.getBoolean(PrefKeys.ENABLE_READAHEAD, PrefDefaults.ENABLE_READAHEAD);
} else if (PrefKeys.AUTOPLAYLIST_PLAYCOUNTS.equals(key)) {
mAutoPlPlaycounts = settings.getInt(PrefKeys.AUTOPLAYLIST_PLAYCOUNTS, PrefDefaults.AUTOPLAYLIST_PLAYCOUNTS);
} else if (PrefKeys.PLAYLIST_SYNC_MODE.equals(key)) {
} else if (PrefKeys.PLAYLIST_SYNC_MODE.equals(key) || PrefKeys.PLAYLIST_SYNC_FOLDER.equals(key)) {
int syncMode = Integer.parseInt(settings.getString(PrefKeys.PLAYLIST_SYNC_MODE, PrefDefaults.PLAYLIST_SYNC_MODE));
mPlaylistObserver.setSyncMode(syncMode);
String syncFolder = settings.getString(PrefKeys.PLAYLIST_SYNC_FOLDER, PrefDefaults.PLAYLIST_SYNC_FOLDER);
mPlaylistObserver.unregister();
mPlaylistObserver = new PlaylistObserver(this, syncFolder, syncMode);
} else if (PrefKeys.SELECTED_THEME.equals(key) || PrefKeys.DISPLAY_MODE.equals(key)) {
// Theme changed: trigger a restart of all registered activites
ArrayList<TimelineCallback> list = sCallbacks;

View File

@ -72,10 +72,14 @@ public class PlaylistObserver extends SQLiteOpenHelper implements Handler.Callba
* Handler thread used to perform playlist sync.
*/
private Handler mHandler;
/**
* Observer which watches the playlist directory.
*/
private FileObserver mFileObserver;
/**
* Directory which holds observed playlists.
*/
private File mPlaylists = new File(Environment.getExternalStorageDirectory(), "Playlists");
private File mPlaylists;
/**
* What kind of synching to perform, bitmask of PlaylistObserver.SYNC_MODE_*
*/
@ -96,10 +100,11 @@ public class PlaylistObserver extends SQLiteOpenHelper implements Handler.Callba
}
public PlaylistObserver(Context context, int mode) {
public PlaylistObserver(Context context, String folder, int mode) {
super(context, "playlist_observer.db", null, 1 /* version */);
mContext = context;
setSyncMode(mode);
mSyncMode = mode;
mPlaylists = new File(folder);
// Launch new thread for background execution
mHandlerThread= new HandlerThread("PlaylisObserverHandler", Process.THREAD_PRIORITY_LOWEST);
@ -108,6 +113,8 @@ public class PlaylistObserver extends SQLiteOpenHelper implements Handler.Callba
// Register to receive media library events.
MediaLibrary.registerLibraryObserver(mLibraryObserver);
// Create and start directory observer.
mFileObserver = getFileObserver(mPlaylists);
mFileObserver.startWatching();
XT("Object created, trigger FULL_SYNC_SCAN");
@ -127,16 +134,6 @@ public class PlaylistObserver extends SQLiteOpenHelper implements Handler.Callba
mHandler = null;
}
/**
* Change the sync mode of a created instance
*
* @param mode the new mode
*/
public void setSyncMode(int mode) {
mSyncMode = mode;
XT("Sync mode is now "+mSyncMode);
}
/**
* SQLiteHelper onCreate
*/
@ -561,25 +558,29 @@ public class PlaylistObserver extends SQLiteOpenHelper implements Handler.Callba
* @param event the event type
* @param dirent the filename which triggered the event.
*/
private final static int mask = FileObserver.CLOSE_WRITE | FileObserver.MOVED_FROM | FileObserver.MOVED_TO | FileObserver.DELETE;
private final FileObserver mFileObserver = new FileObserver(mPlaylists.getAbsolutePath(), mask) {
@Override
public void onEvent(int event, String dirent) {
if (!isM3uFilename(dirent))
return;
private FileObserver getFileObserver(File target) {
final int mask = FileObserver.CLOSE_WRITE | FileObserver.MOVED_FROM | FileObserver.MOVED_TO | FileObserver.DELETE;
XT("new file observer at "+target+" with mask "+mask);
if ((event & (FileObserver.MOVED_FROM | FileObserver.DELETE)) != 0) {
// A M3U vanished, do a full scan.
XT("FileObserver::onEvent DELETE of "+dirent+" triggers FULL_SYNC_SCAN");
sendUniqueMessage(MSG_FULL_SYNC_SCAN, 0);
return new FileObserver(target.getAbsolutePath(), mask) {
@Override
public void onEvent(int event, String dirent) {
if (!isM3uFilename(dirent))
return;
if ((event & (FileObserver.MOVED_FROM | FileObserver.DELETE)) != 0) {
// A M3U vanished, do a full scan.
XT("FileObserver::onEvent DELETE of "+dirent+" triggers FULL_SYNC_SCAN");
sendUniqueMessage(MSG_FULL_SYNC_SCAN, 0);
}
if ((event & (FileObserver.MOVED_TO | FileObserver.CLOSE_WRITE)) != 0) {
// Single file was created, import it.
XT("FileObserver::onEvent WRITE of "+dirent+" triggers IMPORT_M3U");
sendUniqueMessage(MSG_IMPORT_M3U, new File(mPlaylists, dirent));
}
}
if ((event & (FileObserver.MOVED_TO | FileObserver.CLOSE_WRITE)) != 0) {
// Single file was created, import it.
XT("FileObserver::onEvent WRITE of "+dirent+" triggers IMPORT_M3U");
sendUniqueMessage(MSG_IMPORT_M3U, new File(mPlaylists, dirent));
}
}
};
};
}
private void XT(String s) {
try(PrintWriter pw = new PrintWriter(new FileOutputStream(new File("/sdcard/playlist-observer.txt"), true))) {

View File

@ -0,0 +1,54 @@
/*
* 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 android.app.Activity;
import android.os.Bundle;
import android.content.SharedPreferences;
import java.io.File;
import java.util.ArrayList;
public class PlaylistObserverDirActivity extends FolderPickerActivity {
/**
* Shared preferences
*/
private SharedPreferences mPrefs;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.filebrowser_start);
mPrefs = PlaybackService.getSettings(this);
// Start at currently configured directory.
String current = mPrefs.getString(PrefKeys.PLAYLIST_SYNC_FOLDER, PrefDefaults.PLAYLIST_SYNC_FOLDER);
setCurrentDir(new File(current));
}
@Override
public void onFolderPicked(File directory, ArrayList<String> a, ArrayList<String> b) {
SharedPreferences.Editor editor = mPrefs.edit();
editor.putString(PrefKeys.PLAYLIST_SYNC_FOLDER, directory.getAbsolutePath());
editor.commit();
finish();
}
}

View File

@ -71,4 +71,5 @@ public class PrefDefaults {
public static final boolean ENABLE_SCROLL_TO_SONG = false;
public static final boolean KEEP_SCREEN_ON = false;
public static final String PLAYLIST_SYNC_MODE = "255";
public static final String PLAYLIST_SYNC_FOLDER = "/sdcard/Playlists";
}

View File

@ -72,4 +72,5 @@ public class PrefKeys {
public static final String ENABLE_SCROLL_TO_SONG = "enable_scroll_to_song";
public static final String KEEP_SCREEN_ON = "keep_screen_on";
public static final String PLAYLIST_SYNC_MODE = "playlist_sync_mode";
public static final String PLAYLIST_SYNC_FOLDER = "playlist_sync_folder";
}

View File

@ -239,6 +239,15 @@ public class PreferencesActivity extends PreferenceActivity
}
}
public static class PlaylistFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState)
{
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preference_playlist);
}
}
public static class HelpFragment extends PreferenceFragment {
@Override
public void onCreate(Bundle savedInstanceState)

View File

@ -331,10 +331,12 @@ THE SOFTWARE.
<string name="autoplaylist_playcounts_name" formatted="false">Top %d</string>
<string name="playlist_sync_mode_title">Playlist synchronization</string>
<string name="playlist_sync_all">Synchronize \'Playlists\' folder</string>
<string name="playlist_sync_all">Full synchronization</string>
<string name="playlist_sync_only_import">Import M3U, never export</string>
<string name="playlist_sync_only_export">Write M3U, never import</string>
<string name="playlist_sync_disabled">Disabled</string>
<string name="playlist_sync_folder_title">Synchronization folder</string>
<string name="playlist_sync_folder_summary">Folder to use for playlist synchronization</string>
<string name="permission_request_summary">Vanilla Music needs read permission to display your music library</string>
<string name="reverse_sort">Reverse sort</string>

View File

@ -45,6 +45,9 @@ THE SOFTWARE.
<header
android:fragment="ch.blinkenlights.android.vanilla.PreferencesMediaLibrary"
android:title="@string/media_library" />
<header
android:fragment="ch.blinkenlights.android.vanilla.PreferencesActivity$PlaylistFragment"
android:title="@string/playlists" />
<header
android:fragment="ch.blinkenlights.android.vanilla.PreferencesTheme"
android:title="@string/theme" />

View File

@ -49,23 +49,6 @@ THE SOFTWARE.
android:title="@string/double_tap_title"
android:summary="@string/double_tap_summary"
android:defaultValue="false" />
<ch.blinkenlights.android.vanilla.SeekBarPreference
android:key="playcounts_autoplaylist"
android:negativeButtonText="@null"
android:dialogLayout="@layout/seekbar_pref"
android:title="@string/autoplaylist_playcounts_title"
android:defaultValue="0"
vanilla:sbpMaxValue="100"
vanilla:sbpSteps="25"
vanilla:sbpSummaryText="@string/autoplaylist_playcounts_summary"
vanilla:sbpSummaryFormat="@string/autoplaylist_playcounts_fmt"
vanilla:sbpSummaryZeroText="@string/autoplaylist_playcounts_disabled"/>
<ch.blinkenlights.android.vanilla.ListPreferenceSummary
android:key="playlist_sync_mode"
android:title="@string/playlist_sync_mode_title"
android:entries="@array/playlist_sync_mode_entries"
android:entryValues="@array/playlist_sync_mode_values"
android:defaultValue="255" />
<CheckBoxPreference
android:key="scrobble"
android:title="@string/scrobble_title"

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8" ?>
<!--
Copyright (C) 2018 Adrian Ulrich <adrian@blinkenlights.ch>
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<PreferenceScreen
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:vanilla="http://schemas.android.com/apk/res-auto"
android:persistent="true">
<ch.blinkenlights.android.vanilla.SeekBarPreference
android:key="playcounts_autoplaylist"
android:negativeButtonText="@null"
android:dialogLayout="@layout/seekbar_pref"
android:title="@string/autoplaylist_playcounts_title"
android:defaultValue="0"
vanilla:sbpMaxValue="100"
vanilla:sbpSteps="25"
vanilla:sbpSummaryText="@string/autoplaylist_playcounts_summary"
vanilla:sbpSummaryFormat="@string/autoplaylist_playcounts_fmt"
vanilla:sbpSummaryZeroText="@string/autoplaylist_playcounts_disabled"/>
<ch.blinkenlights.android.vanilla.ListPreferenceSummary
android:key="playlist_sync_mode"
android:title="@string/playlist_sync_mode_title"
android:entries="@array/playlist_sync_mode_entries"
android:entryValues="@array/playlist_sync_mode_values"
android:defaultValue="255" />
<PreferenceScreen
android:dependency="playlist_sync_mode"
android:title="@string/playlist_sync_folder_title"
android:summary="@string/playlist_sync_folder_summary">
<intent
android:targetPackage="ch.blinkenlights.android.vanilla"
android:targetClass="ch.blinkenlights.android.vanilla.PlaylistObserverDirActivity" />
</PreferenceScreen>
</PreferenceScreen>