import old playlists

This commit is contained in:
Adrian Ulrich 2016-12-31 12:38:05 +01:00
parent 2a562230da
commit d26d10570b
3 changed files with 139 additions and 8 deletions

View File

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

View File

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

View File

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