Merge indexer-select branch.

This adds the ability to include and exclude media folders.
This commit is contained in:
Adrian Ulrich 2017-04-14 16:49:28 +02:00
parent 58068fb9ca
commit 21ce9c1d57
34 changed files with 370 additions and 120 deletions

View File

@ -152,6 +152,8 @@ THE SOFTWARE.
android:name="TabOrderActivity" />
<activity
android:name="FilebrowserStartActivity" />
<activity
android:name="MediaFoldersSelectionActivity" />
<activity
android:name="PermissionRequestActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />

BIN
orig/file_document.svgz Normal file

Binary file not shown.

BIN
orig/file_image.svgz Normal file

Binary file not shown.

BIN
orig/file_music.svgz Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 985 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 757 B

After

Width:  |  Height:  |  Size: 882 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 723 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 934 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 632 B

After

Width:  |  Height:  |  Size: 645 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 875 B

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

After

Width:  |  Height:  |  Size: 1.6 KiB

View File

@ -44,7 +44,7 @@ THE SOFTWARE.
android:layout_height="wrap_content"
android:layout_width="0dp"
android:layout_weight="1"
android:text="@string/select" />
android:text="@string/empty" />
</LinearLayout>
@ -61,7 +61,7 @@ THE SOFTWARE.
android:layout_height="0px"
android:layout_width="fill_parent"
android:layout_weight="1"
android:choiceMode="multipleChoice"
android:choiceMode="none"
dslv:drag_enabled="false" />
</LinearLayout>

View File

@ -39,6 +39,25 @@ along with this program. If not, see <http://www.gnu.org/licenses/>.
android:layout_height="wrap_content"
android:text="@string/media_scan_force_bastp" />
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:textColor="?overlay_foreground_color"
android:text="@string/media_folders_header" />
<TextView
android:id="@+id/media_directories"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="-" />
<Button
android:id="@+id/edit_button"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="end"
android:text="@string/edit">
</Button>
<TextView
style="?android:attr/listSeparatorTextViewStyle"
android:textColor="?overlay_foreground_color"

View File

@ -303,6 +303,7 @@ THE SOFTWARE.
<string name="filebrowser_start">Filebrowser home</string>
<string name="customize_filebrowser_start">Filebrowser starts at this directory</string>
<string name="select">Select</string>
<string name="save">Save</string>
<string name="autoplaylist_playcounts_title">Automatic playlist creation</string>
@ -319,6 +320,7 @@ THE SOFTWARE.
<string name="no_receiving_apps">No receiving apps found for this media type!</string>
<string name="media_library">Media library</string>
<string name="media_folders_header">Indexed directories</string>
<string name="media_scan_header">Media scanner</string>
<string name="media_scan_full">Scan full filesystem (Slow)</string>
<string name="media_scan_drop_db">Flush media database (Dangerzone!)</string>
@ -334,6 +336,11 @@ THE SOFTWARE.
<string name="media_scan_preferences_change_title">Scanner options changed</string>
<string name="media_scan_preferences_change_message">Changing this option will start a full rescan of your library. Would you like to continue?</string>
<!-- Folder selector -->
<string name="hint_long_press_to_modify_folder">Long press to modify folder options</string>
<string name="folder_include">Include</string>
<string name="folder_exclude">Exclude</string>
<string name="folder_neutral">Neutral</string>
<!-- Plugin system -->
<string name="plugins">Plugins</string>
</resources>

View File

@ -23,8 +23,14 @@ import android.database.Cursor;
import android.database.ContentObserver;
import android.provider.MediaStore;
import android.os.Environment;
import android.util.Log;
import java.util.ArrayList;
import java.io.ObjectOutputStream;
import java.io.ObjectInputStream;
import java.io.Serializable;
import java.io.File;
public class MediaLibrary {
@ -37,7 +43,6 @@ public class MediaLibrary {
public static final String TABLE_GENRES_SONGS = "genres_songs";
public static final String TABLE_PLAYLISTS = "playlists";
public static final String TABLE_PLAYLISTS_SONGS = "playlists_songs";
public static final String TABLE_PREFERENCES = "preferences";
public static final String VIEW_ARTISTS = "_artists";
public static final String VIEW_ALBUMARTISTS = "_albumartists";
public static final String VIEW_COMPOSERS = "_composers";
@ -50,17 +55,16 @@ public class MediaLibrary {
public static final int ROLE_COMPOSER = 1;
public static final int ROLE_ALBUMARTIST = 2;
private static final String PREF_KEY_FORCE_BASTP = "force_bastp";
private static final String PREF_KEY_GROUP_ALBUMS = "group_albums";
private static final String PREF_KEY_NATIVE_LIBRARY_COUNT = "native_audio_db_count";
private static final String PREF_KEY_NATIVE_LAST_MTIME = "native_last_mtime";
public static final String PREFERENCES_FILE = "_prefs-v1.obj";
/**
* Options used by the MediaScanner class
*/
public static class Preferences {
public static class Preferences implements Serializable {
public boolean forceBastp;
public boolean groupAlbumsByFolder;
public ArrayList<String> mediaFolders;
public ArrayList<String> blacklistedFolders;
int _nativeLibraryCount;
int _nativeLastMtime;
}
@ -121,17 +125,63 @@ public class MediaLibrary {
public static MediaLibrary.Preferences getPreferences(Context context) {
MediaLibrary.Preferences prefs = sPreferences;
if (prefs == null) {
MediaLibraryBackend backend = getBackend(context);
prefs = new MediaLibrary.Preferences();
prefs.forceBastp = backend.getSetPreference(PREF_KEY_FORCE_BASTP, -1) != 0;
prefs.groupAlbumsByFolder = backend.getSetPreference(PREF_KEY_GROUP_ALBUMS, -1) != 0;
prefs._nativeLibraryCount = backend.getSetPreference(PREF_KEY_NATIVE_LIBRARY_COUNT, -1);
prefs._nativeLastMtime = backend.getSetPreference(PREF_KEY_NATIVE_LAST_MTIME, -1);
try (ObjectInputStream ois = new ObjectInputStream(context.openFileInput(PREFERENCES_FILE))) {
prefs = (MediaLibrary.Preferences)ois.readObject();
} catch (Exception e) {
Log.w("VanillaMusic", "Returning default media-library preferences due to error: "+ e);
}
if (prefs == null)
prefs = new MediaLibrary.Preferences();
if (prefs.mediaFolders == null || prefs.mediaFolders.size() == 0)
prefs.mediaFolders = discoverDefaultMediaPaths();
if (prefs.blacklistedFolders == null) // we allow this to be empty, but it must not be null.
prefs.blacklistedFolders = discoverDefaultBlacklistedPaths();
sPreferences = prefs; // cached for frequent access
}
return prefs;
}
/**
* Returns the guessed media paths for this device
*
* @return array with guessed directories
*/
private static ArrayList<String> discoverDefaultMediaPaths() {
ArrayList<String> defaultPaths = new ArrayList<>();
// this should always exist
defaultPaths.add(Environment.getExternalStorageDirectory().getAbsolutePath());
// this *may* exist
File sdCard = new File("/storage/sdcard1");
if (sdCard.isDirectory())
defaultPaths.add(sdCard.getAbsolutePath());
return defaultPaths;
}
/**
* Returns default paths which should be blacklisted
*
* @return array with guessed blacklist
*/
private static ArrayList<String> discoverDefaultBlacklistedPaths() {
final String[] defaultBlacklistPostfix = { "Android/data", "Alarms", "Notifications", "Ringtones" };
ArrayList<String> defaultPaths = new ArrayList<>();
for (String path : discoverDefaultMediaPaths()) {
for (int i = 0; i < defaultBlacklistPostfix.length; i++) {
File guess = new File(path + "/" + defaultBlacklistPostfix[i]);
if (guess.isDirectory())
defaultPaths.add(guess.getAbsolutePath());
}
}
return defaultPaths;
}
/**
* Updates the scanner preferences
*
@ -141,11 +191,14 @@ public class MediaLibrary {
*/
public static void setPreferences(Context context, MediaLibrary.Preferences prefs) {
MediaLibraryBackend backend = getBackend(context);
backend.getSetPreference(PREF_KEY_FORCE_BASTP, prefs.forceBastp ? 1 : 0);
backend.getSetPreference(PREF_KEY_GROUP_ALBUMS, prefs.groupAlbumsByFolder ? 1 : 0);
backend.getSetPreference(PREF_KEY_NATIVE_LIBRARY_COUNT, prefs._nativeLibraryCount);
backend.getSetPreference(PREF_KEY_NATIVE_LAST_MTIME, prefs._nativeLastMtime);
sPreferences = null;
try (ObjectOutputStream oos = new ObjectOutputStream(context.openFileOutput(PREFERENCES_FILE, 0))) {
oos.writeObject(prefs);
} catch (Exception e) {
Log.w("VanillaMusic", "Failed to store media preferences: " + e);
}
sPreferences = prefs;
}
/**
@ -456,25 +509,6 @@ public class MediaLibrary {
return (hash < 0 ? hash*-1 : hash);
}
/**
* Returns the guessed media paths for this device
*
* @return array with guessed directories
*/
public static File[] discoverMediaPaths() {
ArrayList<File> scanTargets = new ArrayList<>();
// this should always exist
scanTargets.add(Environment.getExternalStorageDirectory());
// this *may* exist
File sdCard = new File("/storage/sdcard1");
if (sdCard.isDirectory())
scanTargets.add(sdCard);
return scanTargets.toArray(new File[scanTargets.size()]);
}
// Columns of Song entries
public interface SongColumns {
/**

View File

@ -35,7 +35,7 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
/**
* The database version we are using
*/
private static final int DATABASE_VERSION = 20170217;
private static final int DATABASE_VERSION = 20170407;
/**
* on-disk file to store the database
*/
@ -104,33 +104,6 @@ public class MediaLibraryBackend extends SQLiteOpenHelper {
return mtime;
}
/**
* Simple interface to set and get preference values
*
* @param stringKey the key to use
* @param newVal the value to set
*
* Note: The new value will only be set if it is >= 0
* Lookup failures will return 0
*/
int getSetPreference(String stringKey, int newVal) {
int oldVal = 0; // this is returned if we found nothing
int key = Math.abs(stringKey.hashCode());
SQLiteDatabase dbh = getWritableDatabase();
Cursor cursor = dbh.query(MediaLibrary.TABLE_PREFERENCES, new String[] { MediaLibrary.PreferenceColumns.VALUE }, MediaLibrary.PreferenceColumns.KEY+"="+key, null, null, null, null, null);
if (cursor.moveToFirst()) {
oldVal = cursor.getInt(0);
}
cursor.close();
if (newVal >= 0 && newVal != oldVal) {
dbh.execSQL("INSERT OR REPLACE INTO "+MediaLibrary.TABLE_PREFERENCES+" ("+MediaLibrary.PreferenceColumns.KEY+", "+MediaLibrary.PreferenceColumns.VALUE+") "
+" VALUES("+key+", "+newVal+")");
}
return oldVal;
}
/**
* Wrapper for SQLiteDatabse.delete() function
*

View File

@ -108,8 +108,9 @@ public class MediaScanner implements Handler.Callback {
* Performs a 'slow' scan by inspecting all files on the device
*/
public void startFullScan() {
for (File dir : MediaLibrary.discoverMediaPaths()) {
mScanPlan.addNextStep(RPC_READ_DIR, dir);
MediaLibrary.Preferences prefs = MediaLibrary.getPreferences(mContext);
for (String path : prefs.mediaFolders) {
mScanPlan.addNextStep(RPC_READ_DIR, new File(path));
}
mScanPlan.addNextStep(RPC_LIBRARY_VRFY, null);
mScanPlan.addNextStep(RPC_NATIVE_VRFY, null);
@ -559,7 +560,6 @@ public class MediaScanner implements Handler.Callback {
}
private static final Pattern sIgnoredFilenames = Pattern.compile("^([^\\.]+|.+\\.(jpe?g|gif|png|bmp|webm|txt|pdf|avi|mp4|mkv|zip|tgz|xml))$", Pattern.CASE_INSENSITIVE);
private static final Pattern sIgnoredDirectories = Pattern.compile("^.+/(Android/data|Alarms|Notifications|Ringtones)/.+$", Pattern.CASE_INSENSITIVE);
/**
* Returns true if the file should not be scanned
*
@ -567,8 +567,30 @@ public class MediaScanner implements Handler.Callback {
* @return boolean
*/
private boolean isBlacklisted(File file) {
boolean blacklisted = sIgnoredFilenames.matcher(file.getName()).matches() || sIgnoredDirectories.matcher(file.getPath()).matches();
return blacklisted;
if (sIgnoredFilenames.matcher(file.getName()).matches())
return true;
int wlPoints = -1;
int blPoints = -1;
for (String path : MediaLibrary.getPreferences(mContext).mediaFolders) {
if (path.length() > wlPoints &&
file.getPath().startsWith(path)) {
wlPoints = path.length();
}
}
for (String path : MediaLibrary.getPreferences(mContext).blacklistedFolders) {
if (path.length() > blPoints &&
file.getPath().startsWith(path)) {
blPoints = path.length();
}
}
// Consider a file to be blacklisted if it is not
// present in any whitelisted dir OR if we found
// a blacklist entry with a longer prefix.
return (wlPoints < 0 || blPoints > wlPoints);
}

View File

@ -116,13 +116,6 @@ public class MediaSchema {
+ MediaLibrary.PlaylistSongColumns.POSITION +" INTEGER NOT NULL "
+ ");";
/**
* SQL schema for our preferences
*/
private static final String DATABASE_CREATE_PREFERENCES = "CREATE TABLE "+ MediaLibrary.TABLE_PREFERENCES + " ("
+ MediaLibrary.PreferenceColumns.KEY +" INTEGER PRIMARY KEY, "
+ MediaLibrary.PreferenceColumns.VALUE +" INTEGER NOT NULL "
+ ");";
/**
* Index to select a playlist quickly
*/
@ -263,7 +256,6 @@ public class MediaSchema {
dbh.execSQL(VIEW_CREATE_ALBUMARTISTS);
dbh.execSQL(VIEW_CREATE_COMPOSERS);
dbh.execSQL(VIEW_CREATE_PLAYLIST_SONGS);
dbh.execSQL(DATABASE_CREATE_PREFERENCES);
}
/**
@ -286,11 +278,6 @@ public class MediaSchema {
dbh.execSQL("UPDATE songs SET disc_num=1 WHERE disc_num IS null");
}
if (oldVersion < 20170120) {
dbh.execSQL(DATABASE_CREATE_PREFERENCES);
triggerFullMediaScan(dbh);
}
if (oldVersion < 20170211) {
// older versions of triggerFullMediaScan did this by mistake
dbh.execSQL("UPDATE songs SET mtime=1 WHERE mtime=0");
@ -302,18 +289,10 @@ public class MediaSchema {
dbh.execSQL(VIEW_CREATE_SONGS_ALBUMS_ARTISTS_HUGE);
}
}
if (oldVersion >= 20170120 && oldVersion < 20170407) {
dbh.execSQL("DROP TABLE preferences");
}
/**
* Changes the mtime of all songs and flushes the scanner progress / preferences
* This triggers a full rebuild of the library on startup
*
* @param dbh the writeable dbh to use
*/
private static void triggerFullMediaScan(SQLiteDatabase dbh) {
dbh.execSQL("UPDATE "+MediaLibrary.TABLE_SONGS+" SET "+MediaLibrary.SongColumns.MTIME+"=1");
// wipes non-bools only - not nice but good enough for now
dbh.execSQL("DELETE FROM "+MediaLibrary.TABLE_PREFERENCES+" WHERE "+MediaLibrary.PreferenceColumns.VALUE+" > 1");
}
}

View File

@ -48,6 +48,8 @@ public class FileSystemAdapter
{
private static final Pattern SPACE_SPLIT = Pattern.compile("\\s+");
private static final Pattern FILE_SEPARATOR = Pattern.compile(File.separator);
private static final Pattern GUESS_MUSIC = Pattern.compile("^([^\\.]+|.+\\.(mp3|ogg|mka|opus|flac|aac|m4a|wav))$", Pattern.CASE_INSENSITIVE);
private static final Pattern GUESS_IMAGE = Pattern.compile("^([^\\.]+|.+\\.(gif|jpe?g|png|bmp|tiff?))$", Pattern.CASE_INSENSITIVE);
/**
* Sort by filename.
@ -243,19 +245,17 @@ public class FileSystemAdapter
holder = new ViewHolder();
row.setTag(holder);
row.getCoverView().setImageDrawable(mFolderIcon);
} else {
row = (DraggableRow)convertView;
holder = (ViewHolder)row.getTag();
}
File file = mFiles[pos];
boolean isDirectory = file.isDirectory();
holder.id = pos;
final File file = mFiles[pos];
row.getTextView().setText(file.getName());
row.getCoverView().setVisibility(isDirectory ? View.VISIBLE : View.GONE);
row.showDragger(isDirectory);
row.showDragger(file.isDirectory());
row.getCoverView().setImageDrawable(getDrawableForFile(file));
return row;
}
@ -283,6 +283,25 @@ public class FileSystemAdapter
return mLimiter;
}
/**
* Returns a drawable for given file.
* This function is rather fast as the file type is guessed
* based on the extension.
*
* @return drawable for the guessed mime type
*/
private Drawable getDrawableForFile(File file) {
int res = R.drawable.file_document;
if (file.isDirectory()) {
res = R.drawable.folder;
} else if (GUESS_MUSIC.matcher(file.getName()).matches()) {
res = R.drawable.file_music;
} else if (GUESS_IMAGE.matcher(file.getName()).matches()) {
res = R.drawable.file_image;
}
return mActivity.getResources().getDrawable(res);
}
/**
* Returns the unixpath represented by this limiter
*

View File

@ -22,6 +22,7 @@ import android.os.Bundle;
import android.content.SharedPreferences;
import java.io.File;
import java.util.ArrayList;
public class FilebrowserStartActivity extends FolderPickerActivity {
@ -30,6 +31,8 @@ public class FilebrowserStartActivity extends FolderPickerActivity {
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.filebrowser_start);
mPrefEditor = PlaybackService.getSettings(this).edit();
// Make sure that we display the current selection
@ -39,7 +42,7 @@ public class FilebrowserStartActivity extends FolderPickerActivity {
@Override
public void onFolderSelected(File directory) {
public void onFolderPicked(File directory, ArrayList<String> a, ArrayList<String> b) {
mPrefEditor.putString(PrefKeys.FILESYSTEM_BROWSE_START, directory.getAbsolutePath());
mPrefEditor.commit();
finish();

View File

@ -18,8 +18,11 @@
package ch.blinkenlights.android.vanilla;
import java.util.Arrays;
import java.util.ArrayList;
import java.io.File;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.DialogInterface;
import android.os.Bundle;
import android.view.View;
import android.view.MenuItem;
@ -33,7 +36,8 @@ import android.widget.Toast;
import com.mobeta.android.dslv.DragSortListView;
public abstract class FolderPickerActivity extends Activity
implements AdapterView.OnItemClickListener
implements AdapterView.OnItemClickListener,
AdapterView.OnItemLongClickListener
{
/**
@ -56,14 +60,26 @@ public abstract class FolderPickerActivity extends Activity
* The array adapter of our listview
*/
private FolderPickerAdapter mListAdapter;
/**
* True if folder-tri-state selection mode
* is enabled
*/
private boolean mTritastic;
/**
* List of included dirs in tristate mode
*/
private ArrayList<String> mIncludedDirs;
/**
* List of excluded dirs in tristate mode
*/
private ArrayList<String> mExcludedDirs;
@Override
public void onCreate(Bundle savedInstanceState) {
ThemeHelper.setTheme(this, R.style.BackActionBar);
super.onCreate(savedInstanceState);
setTitle(R.string.filebrowser_start);
setContentView(R.layout.filebrowser_content);
setContentView(R.layout.folderpicker_content);
mCurrentPath = new File("/");
mListAdapter = new FolderPickerAdapter(this, 0);
@ -76,16 +92,39 @@ public abstract class FolderPickerActivity extends Activity
mSaveButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
onFolderSelected(mCurrentPath);
onFolderPicked(mCurrentPath, mIncludedDirs, mExcludedDirs);
}});
// init defaults
enableTritasticSelect(false, null, null);
}
/**
* Called after a folder was selected
*
* @param directory the selected directory
* @param included unique list of included directories in tristastic mode
* @param excluded unique list of excluded directories in triatastic mode
*/
public abstract void onFolderSelected(File directory);
public abstract void onFolderPicked(File directory, ArrayList<String> included, ArrayList<String> excluded);
/**
* Enables tritastic selection, that is: user can select each
* directory to be in included, excluded or neutral state.
*
* @param enabled enables or disables this feature
* @param included initial list of included dirs
* @param excluded initial list of excluded dirs
*/
public void enableTritasticSelect(boolean enabled, ArrayList<String> included, ArrayList<String> excluded) {
mTritastic = enabled;
mIncludedDirs = (enabled ? included : null);
mExcludedDirs = (enabled ? excluded : null);
mListView.setOnItemLongClickListener(enabled ? this : null);
mSaveButton.setText(enabled ? R.string.save : R.string.select);
if (enabled)
Toast.makeText(this, R.string.hint_long_press_to_modify_folder, Toast.LENGTH_SHORT).show();
}
/**
* Jumps to given directory
@ -94,7 +133,7 @@ public abstract class FolderPickerActivity extends Activity
*/
void setCurrentDirectory(File directory) {
mCurrentPath = directory;
refreshDirectoryList();
refreshDirectoryList(true);
}
/**
@ -104,7 +143,7 @@ public abstract class FolderPickerActivity extends Activity
@Override
public void onResume() {
super.onResume();
refreshDirectoryList();
refreshDirectoryList(false);
}
/**
@ -131,35 +170,83 @@ public abstract class FolderPickerActivity extends Activity
*/
@Override
public void onItemClick(AdapterView<?> parent, View view, int pos, long id) {
String dirent = mListAdapter.getItem(pos);
FolderPickerAdapter.Item item = mListAdapter.getItem(pos);
File newPath = null;
if(pos == 0) {
newPath = mCurrentPath.getParentFile();
}
else {
newPath = new File(mCurrentPath, dirent);
newPath = new File(mCurrentPath, item.name);
}
if (newPath != null)
setCurrentDirectory(newPath);
}
/**
* Called on long-click on a row
*/
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int pos, long id) {
FolderPickerAdapter.Item item = mListAdapter.getItem(pos);
if (item.file == null)
return false;
final String path = item.file.getAbsolutePath();
final CharSequence[] options = new CharSequence[]{
getResources().getString(R.string.folder_include),
getResources().getString(R.string.folder_exclude),
getResources().getString(R.string.folder_neutral)
};
AlertDialog.Builder builder = new AlertDialog.Builder(this)
.setTitle(item.name)
.setItems(options,
new DialogInterface.OnClickListener() {
public void onClick(DialogInterface dialog, int which) {
mIncludedDirs.remove(path);
mExcludedDirs.remove(path);
switch (which) {
case 0:
mIncludedDirs.add(path);
break;
case 1:
mExcludedDirs.add(path);
break;
default:
}
refreshDirectoryList(false);
}
});
builder.create().show();
return true;
}
/**
* display mCurrentPath in the dialog
*/
private void refreshDirectoryList() {
private void refreshDirectoryList(boolean scroll) {
File path = mCurrentPath;
File[]dirs = path.listFiles();
mListAdapter.clear();
mListAdapter.add("../");
mListAdapter.add(new FolderPickerAdapter.Item("../", null, 0));
if(dirs != null) {
Arrays.sort(dirs);
for(File fentry: dirs) {
if(fentry.isDirectory()) {
mListAdapter.add(fentry.getName());
int color = 0;
if (mTritastic) {
if (mIncludedDirs.contains(fentry.getAbsolutePath()))
color = 0xff00c853;
if (mExcludedDirs.contains(fentry.getAbsolutePath()))
color = 0xffd50000;
}
FolderPickerAdapter.Item item = new FolderPickerAdapter.Item(fentry.getName(), fentry, color);
mListAdapter.add(item);
}
}
}
@ -167,7 +254,8 @@ public abstract class FolderPickerActivity extends Activity
Toast.makeText(this, "Failed to display " + path.getAbsolutePath(), Toast.LENGTH_SHORT).show();
}
mPathDisplay.setText(path.getAbsolutePath());
mListView.setSelectionFromTop(0, 0);
if (scroll)
mListView.setSelectionFromTop(0, 0);
}
}

View File

@ -29,10 +29,23 @@ import android.widget.TextView;
import android.widget.ArrayAdapter;
import android.graphics.drawable.Drawable;
import java.io.File;
public class FolderPickerAdapter
extends ArrayAdapter<String>
extends ArrayAdapter<FolderPickerAdapter.Item>
{
public static class Item {
String name;
File file;
int color;
public Item(String name, File file, int color) {
this.name = name;
this.file = file;
this.color = color;
}
}
private final LayoutInflater mInflater;
public FolderPickerAdapter(Context context, int resource) {
@ -47,15 +60,15 @@ public class FolderPickerAdapter
if (convertView == null) {
row = (DraggableRow)mInflater.inflate(R.layout.draggable_row, parent, false);
row.setupLayout(DraggableRow.LAYOUT_LISTVIEW);
row.getCoverView().setImageResource(R.drawable.folder);
} else {
row = (DraggableRow)convertView;
}
String label = getItem(pos);
row.getTextView().setText(label);
Item item = (Item)getItem(pos);
row.getTextView().setText(item.name);
row.getCoverView().setColorFilter(item.color);
return row;
}

View File

@ -0,0 +1,56 @@
/*
* Copyright (C) 2017 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 android.app.Activity;
import android.os.Bundle;
import android.content.SharedPreferences;
import java.io.File;
import java.util.ArrayList;
public class MediaFoldersSelectionActivity extends FolderPickerActivity {
private SharedPreferences.Editor mPrefEditor;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setTitle(R.string.media_folders_header);
MediaLibrary.Preferences prefs = MediaLibrary.getPreferences(this);
File startPath = FileUtils.getFilesystemBrowseStart(this);
// Make sure that we display the current selection
setCurrentDirectory(startPath);
enableTritasticSelect(true, prefs.mediaFolders, prefs.blacklistedFolders);
}
@Override
public void onFolderPicked(File directory, ArrayList<String> included, ArrayList<String> excluded) {
MediaLibrary.Preferences prefs = MediaLibrary.getPreferences(this);
prefs.mediaFolders = included;
prefs.blacklistedFolders = excluded;
MediaLibrary.setPreferences(this, prefs);
finish();
}
}

View File

@ -23,6 +23,7 @@ import android.app.AlertDialog;
import android.app.Fragment;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.database.Cursor;
import android.os.Bundle;
import android.view.LayoutInflater;
@ -49,6 +50,10 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
* The cancel button
*/
private View mCancelButton;
/**
* The edit-media-folders button
*/
private View mEditButton;
/**
* The debug / progress text describing the scan status
*/
@ -65,6 +70,10 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
* The number of hours of music we have
*/
private TextView mStatsPlaytime;
/**
* A list of scanned media directories
*/
private TextView mMediaDirectories;
/**
* Checkbox for full scan
*/
@ -85,6 +94,10 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
* Set if we should start a full scan due to option changes
*/
private boolean mFullScanPending;
/**
* Set if we are in the edit dialog
*/
private boolean mIsEditingDirectories;
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
@ -97,10 +110,12 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
mStartButton = (View)view.findViewById(R.id.start_button);
mCancelButton = (View)view.findViewById(R.id.cancel_button);
mEditButton = (View)view.findViewById(R.id.edit_button);
mProgressText = (TextView)view.findViewById(R.id.media_stats_progress_text);
mProgressBar = (ProgressBar)view.findViewById(R.id.media_stats_progress_bar);
mStatsTracks = (TextView)view.findViewById(R.id.media_stats_tracks);
mStatsPlaytime = (TextView)view.findViewById(R.id.media_stats_playtime);
mMediaDirectories = (TextView)view.findViewById(R.id.media_directories);
mFullScanCheck = (CheckBox)view.findViewById(R.id.media_scan_full);
mDropDbCheck = (CheckBox)view.findViewById(R.id.media_scan_drop_db);
mGroupAlbumsCheck = (CheckBox)view.findViewById(R.id.media_scan_group_albums);
@ -109,6 +124,7 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
// Bind onClickListener to some elements
mStartButton.setOnClickListener(this);
mCancelButton.setOnClickListener(this);
mEditButton.setOnClickListener(this);
mGroupAlbumsCheck.setOnClickListener(this);
mForceBastpCheck.setOnClickListener(this);
}
@ -129,6 +145,9 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
});
}}), 0, 200);
if (mIsEditingDirectories)
mIsEditingDirectories = false;
updatePreferences(null);
}
@ -140,7 +159,7 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
mTimer = null;
}
if (mFullScanPending) {
if (mFullScanPending && !mIsEditingDirectories) {
MediaLibrary.startLibraryScan(getActivity(), true, true);
mFullScanPending = false;
}
@ -155,6 +174,11 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
case R.id.cancel_button:
cancelButtonPressed(view);
break;
case R.id.edit_button:
mIsEditingDirectories = true;
mFullScanPending = true;
startActivity(new Intent(getActivity(), MediaFoldersSelectionActivity.class));
break;
case R.id.media_scan_group_albums:
case R.id.media_scan_force_bastp:
confirmUpdatePreferences((CheckBox)view);
@ -212,6 +236,16 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
mGroupAlbumsCheck.setChecked(prefs.groupAlbumsByFolder);
mForceBastpCheck.setChecked(prefs.forceBastp);
String txt = "";
for (String path : prefs.mediaFolders) {
txt += "" + path + "\n";
}
for (String path : prefs.blacklistedFolders) {
txt += "" + path + "\n";
}
mMediaDirectories.setText(txt);
}
/**
@ -227,6 +261,7 @@ public class PreferencesMediaLibrary extends Fragment implements View.OnClickLis
mProgressBar.setProgress(progress.seen);
mStartButton.setEnabled(idle);
mEditButton.setEnabled(idle);
mDropDbCheck.setEnabled(idle);
mFullScanCheck.setEnabled(idle);
mForceBastpCheck.setEnabled(idle);