import old playlists
This commit is contained in:
parent
2a562230da
commit
d26d10570b
@ -313,4 +313,7 @@ THE SOFTWARE.
|
||||
<string name="media_stats_tracks">Number of tracks</string>
|
||||
<string name="media_stats_playtime">Play time (Hours)</string>
|
||||
<string name="media_stats_progress">Scan progress</string>
|
||||
|
||||
<string name="media_library_import_started">Scanning your media library, this might take some time</string>
|
||||
<string name="media_library_import_ended">Media library scan finished!</string>
|
||||
</resources>
|
||||
|
@ -17,6 +17,8 @@
|
||||
|
||||
package ch.blinkenlights.android.medialibrary;
|
||||
|
||||
import ch.blinkenlights.android.vanilla.R;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContentValues;
|
||||
import android.content.SharedPreferences;
|
||||
@ -29,6 +31,8 @@ import android.os.HandlerThread;
|
||||
import android.os.Message;
|
||||
import android.os.Process;
|
||||
|
||||
import android.widget.Toast;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.ArrayList;
|
||||
import java.util.regex.Pattern;
|
||||
@ -50,6 +54,11 @@ public class MediaScanner implements Handler.Callback {
|
||||
* Instance of a media backend
|
||||
*/
|
||||
private MediaLibraryBackend mBackend;
|
||||
/**
|
||||
* True if this is a from-scratch import
|
||||
* Set by KICKSTART rpc
|
||||
*/
|
||||
private boolean mIsInitialScan;
|
||||
|
||||
|
||||
MediaScanner(Context context, MediaLibraryBackend backend) {
|
||||
@ -77,7 +86,7 @@ public class MediaScanner implements Handler.Callback {
|
||||
public void startNormalScan() {
|
||||
mScanPlan.addNextStep(RPC_NATIVE_VRFY, null)
|
||||
.addNextStep(RPC_LIBRARY_VRFY, null);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_NOOP, 0));
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_KICKSTART, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -89,7 +98,7 @@ public class MediaScanner implements Handler.Callback {
|
||||
}
|
||||
mScanPlan.addNextStep(RPC_LIBRARY_VRFY, null);
|
||||
mScanPlan.addNextStep(RPC_NATIVE_VRFY, null);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_NOOP, 0));
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_KICKSTART, 0));
|
||||
}
|
||||
|
||||
/**
|
||||
@ -102,7 +111,7 @@ public class MediaScanner implements Handler.Callback {
|
||||
if (!mHandler.hasMessages(MSG_SCAN_RPC)) {
|
||||
mScanPlan.addNextStep(RPC_NATIVE_VRFY, null)
|
||||
.addOptionalStep(RPC_LIBRARY_VRFY, null); // only runs if previous scan found no change
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_NOOP, 0), delay);
|
||||
mHandler.sendMessageDelayed(mHandler.obtainMessage(MSG_SCAN_RPC, RPC_KICKSTART, 0), delay);
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,8 +134,9 @@ public class MediaScanner implements Handler.Callback {
|
||||
}
|
||||
|
||||
private static final int MSG_SCAN_RPC = 0;
|
||||
private static final int MSG_NOTIFY_CHANGE = 1;
|
||||
private static final int RPC_NOOP = 100;
|
||||
private static final int MSG_SCAN_FINISHED = 1;
|
||||
private static final int MSG_NOTIFY_CHANGE = 2;
|
||||
private static final int RPC_KICKSTART = 100;
|
||||
private static final int RPC_READ_DIR = 101;
|
||||
private static final int RPC_INSPECT_FILE = 102;
|
||||
private static final int RPC_LIBRARY_VRFY = 103;
|
||||
@ -141,8 +151,20 @@ public class MediaScanner implements Handler.Callback {
|
||||
MediaLibrary.notifyObserver();
|
||||
break;
|
||||
}
|
||||
case RPC_NOOP: {
|
||||
// just used to trigger the initial scan
|
||||
case MSG_SCAN_FINISHED: {
|
||||
if (mIsInitialScan) {
|
||||
mIsInitialScan = false;
|
||||
PlaylistBridge.importAndroidPlaylists(mContext);
|
||||
toastMsg(R.string.media_library_import_ended);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPC_KICKSTART: {
|
||||
// a new scan was triggered: check if this is a 'initial / from scratch' scan
|
||||
if (!mIsInitialScan && getSetScanMark(-1) == 0) {
|
||||
mIsInitialScan = true;
|
||||
toastMsg(R.string.media_library_import_started);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case RPC_INSPECT_FILE: {
|
||||
@ -174,7 +196,7 @@ public class MediaScanner implements Handler.Callback {
|
||||
if (message.what == MSG_SCAN_RPC && !mHandler.hasMessages(MSG_SCAN_RPC)) {
|
||||
MediaScanPlan.Step step = mScanPlan.getNextStep();
|
||||
if (step == null) {
|
||||
Log.v("VanillaMusic", "--- all scanners finished ---");
|
||||
mHandler.sendEmptyMessage(MSG_SCAN_FINISHED);
|
||||
} else {
|
||||
Log.v("VanillaMusic", "--- starting scan of type "+step.msg);
|
||||
mHandler.sendMessage(mHandler.obtainMessage(MSG_SCAN_RPC, step.msg, 0, step.arg));
|
||||
@ -423,6 +445,17 @@ public class MediaScanner implements Handler.Callback {
|
||||
return oldVal;
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a toast message
|
||||
* Not sure if this should really be here - a callback to the
|
||||
* observer would probably be nicer
|
||||
*
|
||||
* @param id the message id to display
|
||||
*/
|
||||
private void toastMsg(int resId) {
|
||||
Toast.makeText(mContext, resId, Toast.LENGTH_SHORT).show();
|
||||
}
|
||||
|
||||
// MediaScanPlan describes how we are going to perform the media scan
|
||||
class MediaScanPlan {
|
||||
class Step {
|
||||
|
@ -0,0 +1,95 @@
|
||||
/*
|
||||
* Copyright (C) 2016 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.medialibrary;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.ContentResolver;
|
||||
import android.database.Cursor;
|
||||
import android.net.Uri;
|
||||
import android.provider.MediaStore.Audio;
|
||||
import android.util.Log;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
||||
class PlaylistBridge {
|
||||
|
||||
/**
|
||||
* Queries all native playlists and imports them
|
||||
*
|
||||
* @param context the context to use
|
||||
*/
|
||||
static void importAndroidPlaylists(Context context) {
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = resolver.query(Audio.Playlists.EXTERNAL_CONTENT_URI, new String[]{Audio.Playlists._ID, Audio.Playlists.NAME}, null, null, null);
|
||||
} catch (SecurityException e) {
|
||||
Log.v("VanillaMusic", "Unable to query existing playlists, exception: "+e);
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
long playlistId = cursor.getLong(0);
|
||||
String playlistName = cursor.getString(1);
|
||||
importAndroidPlaylist(context, playlistName, playlistId);
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Imports a single native playlist into our own media library
|
||||
*
|
||||
* @param context the context to use
|
||||
* @param targetName the name of the playlist in our media store
|
||||
* @param playlistId the native playlist id to import
|
||||
*/
|
||||
static void importAndroidPlaylist(Context context, String targetName, long playlistId) {
|
||||
ArrayList<Long> bulkIds = new ArrayList<>();
|
||||
ContentResolver resolver = context.getContentResolver();
|
||||
Uri uri = Audio.Playlists.Members.getContentUri("external", playlistId);
|
||||
Cursor cursor = null;
|
||||
|
||||
try {
|
||||
cursor = resolver.query(uri, new String[]{Audio.Media.DATA}, null, null, Audio.Playlists.Members.DEFAULT_SORT_ORDER);
|
||||
} catch (SecurityException e) {
|
||||
Log.v("VanillaMusic", "Failed to query playlist: "+e);
|
||||
}
|
||||
|
||||
if (cursor != null) {
|
||||
while (cursor.moveToNext()) {
|
||||
String path = cursor.getString(0);
|
||||
// We do not need to do a lookup by path as we can calculate the id used
|
||||
// by the mediastore using the path
|
||||
bulkIds.add(MediaLibrary.hash63(path));
|
||||
}
|
||||
cursor.close();
|
||||
}
|
||||
|
||||
if (bulkIds.size() == 0)
|
||||
return; // do not import empty playlists
|
||||
|
||||
long targetPlaylistId = MediaLibrary.createPlaylist(context, targetName);
|
||||
if (targetPlaylistId == -1)
|
||||
return; // already exists, won't touch
|
||||
|
||||
MediaLibrary.addToPlaylist(context, targetPlaylistId, bulkIds);
|
||||
}
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user