Made progress with the folder feature.
This commit is contained in:
parent
56d3a2dbce
commit
1e6ac6a227
38 changed files with 1423 additions and 148 deletions
|
|
@ -10,10 +10,10 @@ import android.util.SparseArray;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AlbumsPagerFragment;
|
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AlbumsFragment;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.ArtistsPagerFragment;
|
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.ArtistsFragment;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.PlaylistsPagerFragment;
|
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.PlaylistsFragment;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.SongsPagerFragment;
|
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.SongsFragment;
|
||||||
|
|
||||||
import java.lang.ref.WeakReference;
|
import java.lang.ref.WeakReference;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
@ -107,10 +107,10 @@ public class MusicLibraryPagerAdapter extends FragmentPagerAdapter {
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MusicFragments {
|
public enum MusicFragments {
|
||||||
SONG(SongsPagerFragment.class),
|
SONG(SongsFragment.class),
|
||||||
ALBUM(AlbumsPagerFragment.class),
|
ALBUM(AlbumsFragment.class),
|
||||||
ARTIST(ArtistsPagerFragment.class),
|
ARTIST(ArtistsFragment.class),
|
||||||
PLAYLIST(PlaylistsPagerFragment.class);
|
PLAYLIST(PlaylistsFragment.class);
|
||||||
|
|
||||||
private final Class<? extends Fragment> mFragmentClass;
|
private final Class<? extends Fragment> mFragmentClass;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,211 @@
|
||||||
|
package com.kabouzeid.gramophone.adapter;
|
||||||
|
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.support.annotation.LayoutRes;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.v7.app.AppCompatActivity;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
|
||||||
|
import com.bumptech.glide.Glide;
|
||||||
|
import com.bumptech.glide.load.engine.DiskCacheStrategy;
|
||||||
|
import com.bumptech.glide.signature.MediaStoreSignature;
|
||||||
|
import com.kabouzeid.appthemehelper.util.ATHUtil;
|
||||||
|
import com.kabouzeid.gramophone.R;
|
||||||
|
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
|
||||||
|
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
|
||||||
|
import com.kabouzeid.gramophone.glide.audiocover.AudioFileCover;
|
||||||
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
|
import com.kabouzeid.gramophone.util.Util;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.text.DecimalFormat;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
|
||||||
|
public class SongFileAdapter extends AbsMultiSelectAdapter<SongFileAdapter.ViewHolder, File> {
|
||||||
|
|
||||||
|
private static final int FILE = 0;
|
||||||
|
private static final int FOLDER = 1;
|
||||||
|
|
||||||
|
private final AppCompatActivity activity;
|
||||||
|
private ArrayList<File> dataSet;
|
||||||
|
private final int itemLayoutRes;
|
||||||
|
@Nullable
|
||||||
|
private final Callbacks callbacks;
|
||||||
|
@Nullable
|
||||||
|
private final CabHolder cabHolder;
|
||||||
|
|
||||||
|
public SongFileAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList<File> dataSet, @LayoutRes int itemLayoutRes, @Nullable Callbacks callback, @Nullable CabHolder cabHolder) {
|
||||||
|
super(activity, cabHolder, R.menu.menu_media_selection);
|
||||||
|
this.activity = activity;
|
||||||
|
this.dataSet = dataSet;
|
||||||
|
this.itemLayoutRes = itemLayoutRes;
|
||||||
|
this.callbacks = callback;
|
||||||
|
this.cabHolder = cabHolder;
|
||||||
|
setHasStableIds(true);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemViewType(int position) {
|
||||||
|
return dataSet.get(position).isDirectory() ? FOLDER : FILE;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return dataSet.get(position).hashCode();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void swapDataSet(@NonNull ArrayList<File> files) {
|
||||||
|
this.dataSet = files;
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
|
||||||
|
return new ViewHolder(LayoutInflater.from(activity).inflate(itemLayoutRes, parent, false));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onBindViewHolder(final ViewHolder holder, int index) {
|
||||||
|
final File file = dataSet.get(index);
|
||||||
|
|
||||||
|
holder.itemView.setActivated(isChecked(file));
|
||||||
|
|
||||||
|
if (holder.getAdapterPosition() == getItemCount() - 1) {
|
||||||
|
if (holder.shortSeparator != null) {
|
||||||
|
holder.shortSeparator.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (holder.shortSeparator != null) {
|
||||||
|
holder.shortSeparator.setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder.title != null) {
|
||||||
|
holder.title.setText(getFileTitle(file));
|
||||||
|
}
|
||||||
|
if (holder.text != null) {
|
||||||
|
if (holder.getItemViewType() == FILE) {
|
||||||
|
holder.text.setText(getFileText(file));
|
||||||
|
} else {
|
||||||
|
holder.text.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holder.image != null) {
|
||||||
|
loadFileImage(file, holder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getFileTitle(File file) {
|
||||||
|
return file.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected String getFileText(File file) {
|
||||||
|
return file.isDirectory() ? null : readableFileSize(file.length());
|
||||||
|
}
|
||||||
|
|
||||||
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
protected void loadFileImage(File file, final ViewHolder holder) {
|
||||||
|
final int iconColor = ATHUtil.resolveColor(activity, R.attr.iconColor);
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
holder.image.setColorFilter(iconColor, PorterDuff.Mode.SRC_IN);
|
||||||
|
holder.image.setImageResource(R.drawable.ic_folder_white_24dp);
|
||||||
|
} else {
|
||||||
|
Drawable error = Util.getTintedDrawable(activity, R.drawable.ic_music_note_white_24dp, iconColor);
|
||||||
|
Glide.with(activity)
|
||||||
|
.load(new AudioFileCover(file.getPath()))
|
||||||
|
.diskCacheStrategy(DiskCacheStrategy.NONE)
|
||||||
|
.error(error)
|
||||||
|
.placeholder(error)
|
||||||
|
.animate(android.R.anim.fade_in)
|
||||||
|
.signature(new MediaStoreSignature("", file.lastModified(), 0))
|
||||||
|
.into(holder.image);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static String readableFileSize(long size) {
|
||||||
|
if (size <= 0) return size + " B";
|
||||||
|
final String[] units = new String[]{"B", "KB", "MB", "GB", "TB"};
|
||||||
|
int digitGroups = (int) (Math.log10(size) / Math.log10(1024));
|
||||||
|
return new DecimalFormat("#,##0.##").format(size / Math.pow(1024, digitGroups)) + " " + units[digitGroups];
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getItemCount() {
|
||||||
|
return dataSet.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected File getIdentifier(int position) {
|
||||||
|
return dataSet.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected String getName(File object) {
|
||||||
|
return getFileTitle(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onMultipleItemAction(MenuItem menuItem, ArrayList<File> selection) {
|
||||||
|
if (callbacks == null) return;
|
||||||
|
switch (menuItem.getItemId()) {
|
||||||
|
case R.id.action_add_to_current_playing:
|
||||||
|
callbacks.onAddToCurrentPlaying(selection);
|
||||||
|
break;
|
||||||
|
case R.id.action_add_to_playlist:
|
||||||
|
callbacks.onAddToPlaylist(selection);
|
||||||
|
break;
|
||||||
|
case R.id.action_delete_from_device:
|
||||||
|
callbacks.onDeleteFromDevice(selection);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public class ViewHolder extends MediaEntryViewHolder {
|
||||||
|
|
||||||
|
public ViewHolder(View itemView) {
|
||||||
|
super(itemView);
|
||||||
|
if (menu != null && callbacks != null) {
|
||||||
|
menu.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
callbacks.onFileMenuClicked(dataSet.get(getAdapterPosition()));
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (isInQuickSelectMode()) {
|
||||||
|
toggleChecked(getAdapterPosition());
|
||||||
|
} else {
|
||||||
|
if (callbacks != null) {
|
||||||
|
callbacks.onFileSelected(dataSet.get(getAdapterPosition()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onLongClick(View view) {
|
||||||
|
return toggleChecked(getAdapterPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface Callbacks {
|
||||||
|
void onFileSelected(File file);
|
||||||
|
|
||||||
|
void onFileMenuClicked(File file);
|
||||||
|
|
||||||
|
void onAddToPlaylist(ArrayList<File> files);
|
||||||
|
|
||||||
|
void onAddToCurrentPlaying(ArrayList<File> files);
|
||||||
|
|
||||||
|
void onDeleteFromDevice(ArrayList<File> files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -168,7 +168,7 @@ public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder,
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Album> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Album> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case R.id.action_delete_from_disk:
|
case R.id.action_delete_from_device:
|
||||||
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_to_playlist:
|
case R.id.action_add_to_playlist:
|
||||||
|
|
|
||||||
|
|
@ -173,7 +173,7 @@ public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolde
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Artist> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Artist> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case R.id.action_delete_from_disk:
|
case R.id.action_delete_from_device:
|
||||||
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
DeleteSongsDialog.create(getSongList(selection)).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_to_playlist:
|
case R.id.action_add_to_playlist:
|
||||||
|
|
|
||||||
|
|
@ -136,7 +136,7 @@ public class ArtistSongAdapter extends ArrayAdapter<Song> implements MaterialCab
|
||||||
|
|
||||||
private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
private void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case R.id.action_delete_from_disk:
|
case R.id.action_delete_from_device:
|
||||||
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_to_playlist:
|
case R.id.action_add_to_playlist:
|
||||||
|
|
|
||||||
|
|
@ -172,7 +172,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
|
||||||
@Override
|
@Override
|
||||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
||||||
switch (menuItem.getItemId()) {
|
switch (menuItem.getItemId()) {
|
||||||
case R.id.action_delete_from_disk:
|
case R.id.action_delete_from_device:
|
||||||
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
DeleteSongsDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
break;
|
break;
|
||||||
case R.id.action_add_to_playlist:
|
case R.id.action_add_to_playlist:
|
||||||
|
|
|
||||||
|
|
@ -33,7 +33,7 @@ public class SongMenuHelper {
|
||||||
case R.id.action_share:
|
case R.id.action_share:
|
||||||
activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song), null));
|
activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song), null));
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_delete_from_disk:
|
case R.id.action_delete_from_device:
|
||||||
DeleteSongsDialog.create(song).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
DeleteSongsDialog.create(song).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||||
return true;
|
return true;
|
||||||
case R.id.action_add_to_playlist:
|
case R.id.action_add_to_playlist:
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,7 @@
|
||||||
package com.kabouzeid.gramophone.interfaces;
|
package com.kabouzeid.gramophone.interfaces;
|
||||||
|
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
|
||||||
import com.afollestad.materialcab.MaterialCab;
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
@ -7,5 +9,6 @@ import com.afollestad.materialcab.MaterialCab;
|
||||||
*/
|
*/
|
||||||
public interface CabHolder {
|
public interface CabHolder {
|
||||||
|
|
||||||
|
@NonNull
|
||||||
MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback);
|
MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -83,12 +83,12 @@ public class SongLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Cursor makeSongCursor(@NonNull final Context context, final String selection, final String[] values) {
|
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable final String selection, final String[] selectionValues) {
|
||||||
return makeSongCursor(context, selection, values, PreferenceUtil.getInstance(context).getSongSortOrder());
|
return makeSongCursor(context, selection, selectionValues, PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable final String selection, final String[] values, final String sortOrder) {
|
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable final String selection, final String[] selectionValues, final String sortOrder) {
|
||||||
String baseSelection = BASE_SELECTION;
|
String baseSelection = BASE_SELECTION;
|
||||||
if (selection != null && !selection.trim().equals("")) {
|
if (selection != null && !selection.trim().equals("")) {
|
||||||
baseSelection += " AND " + selection;
|
baseSelection += " AND " + selection;
|
||||||
|
|
@ -109,7 +109,7 @@ public class SongLoader {
|
||||||
AudioColumns.ARTIST_ID,// 9
|
AudioColumns.ARTIST_ID,// 9
|
||||||
AudioColumns.ARTIST,// 10
|
AudioColumns.ARTIST,// 10
|
||||||
|
|
||||||
}, baseSelection, values, sortOrder);
|
}, baseSelection, selectionValues, sortOrder);
|
||||||
} catch (SecurityException e) {
|
} catch (SecurityException e) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,8 @@ import android.support.annotation.Nullable;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
|
||||||
|
import hugo.weaving.DebugLog;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||||
|
|
@ -36,97 +37,77 @@ public class SortedCursor extends AbstractCursor {
|
||||||
// the map of external indices to internal indices
|
// the map of external indices to internal indices
|
||||||
private ArrayList<Integer> mOrderedPositions;
|
private ArrayList<Integer> mOrderedPositions;
|
||||||
// this contains the ids that weren't found in the underlying cursor
|
// this contains the ids that weren't found in the underlying cursor
|
||||||
private ArrayList<Long> mMissingIds;
|
private ArrayList<String> mMissingValues;
|
||||||
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||||
private HashMap<Long, Integer> mMapCursorPositions;
|
private HashMap<String, Integer> mMapCursorPositions;
|
||||||
// extra we want to store with the cursor
|
|
||||||
private ArrayList<Object> mExtraData;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @param cursor to wrap
|
* @param cursor to wrap
|
||||||
* @param order the list of unique ids in sorted order to display
|
* @param order the list of unique ids in sorted order to display
|
||||||
* @param columnName the column name of the id to look up in the internal cursor
|
* @param columnName the column name of the id to look up in the internal cursor
|
||||||
*/
|
*/
|
||||||
public SortedCursor(final Cursor cursor, final long[] order, final String columnName,
|
@DebugLog
|
||||||
final List<?> extraData) {
|
public SortedCursor(@NonNull final Cursor cursor, @Nullable final String[] order, final String columnName) {
|
||||||
if (cursor == null) {
|
|
||||||
throw new IllegalArgumentException("Non-null cursor is needed");
|
|
||||||
}
|
|
||||||
|
|
||||||
mCursor = cursor;
|
mCursor = cursor;
|
||||||
mMissingIds = buildCursorPositionMapping(order, columnName, extraData);
|
mMissingValues = buildCursorPositionMapping(order, columnName);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This function populates mOrderedPositions with the cursor positions in the order based
|
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||||
* on the order passed in
|
* on the order passed in
|
||||||
*
|
*
|
||||||
* @param order the target order of the internal cursor
|
* @param order the target order of the internal cursor
|
||||||
* @param extraData Extra data we want to add to the cursor
|
|
||||||
* @return returns the ids that aren't found in the underlying cursor
|
* @return returns the ids that aren't found in the underlying cursor
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
private ArrayList<Long> buildCursorPositionMapping(@Nullable final long[] order,
|
private ArrayList<String> buildCursorPositionMapping(@Nullable final String[] order, final String columnName) {
|
||||||
final String columnName, @Nullable final List<?> extraData) {
|
ArrayList<String> missingValues = new ArrayList<>();
|
||||||
ArrayList<Long> missingIds = new ArrayList<>();
|
|
||||||
|
|
||||||
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||||
mExtraData = new ArrayList<>();
|
|
||||||
|
|
||||||
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||||
final int idPosition = mCursor.getColumnIndex(columnName);
|
final int valueColumnIndex = mCursor.getColumnIndex(columnName);
|
||||||
|
|
||||||
if (mCursor.moveToFirst()) {
|
if (mCursor.moveToFirst()) {
|
||||||
// first figure out where each of the ids are in the cursor
|
// first figure out where each of the ids are in the cursor
|
||||||
do {
|
do {
|
||||||
mMapCursorPositions.put(mCursor.getLong(idPosition), mCursor.getPosition());
|
mMapCursorPositions.put(mCursor.getString(valueColumnIndex), mCursor.getPosition());
|
||||||
} while (mCursor.moveToNext());
|
} while (mCursor.moveToNext());
|
||||||
|
|
||||||
// now create the ordered positions to map to the internal cursor given the
|
if (order != null) {
|
||||||
// external sort order
|
// now create the ordered positions to map to the internal cursor given the
|
||||||
for (int i = 0; order != null && i < order.length; i++) {
|
// external sort order
|
||||||
final long id = order[i];
|
for (final String value : order) {
|
||||||
if (mMapCursorPositions.containsKey(id)) {
|
if (mMapCursorPositions.containsKey(value)) {
|
||||||
mOrderedPositions.add(mMapCursorPositions.get(id));
|
mOrderedPositions.add(mMapCursorPositions.get(value));
|
||||||
mMapCursorPositions.remove(id);
|
mMapCursorPositions.remove(value);
|
||||||
if (extraData != null) {
|
} else {
|
||||||
mExtraData.add(extraData.get(i));
|
missingValues.add(value);
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
missingIds.add(id);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mCursor.moveToFirst();
|
mCursor.moveToFirst();
|
||||||
}
|
}
|
||||||
|
|
||||||
return missingIds;
|
return missingValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the list of ids that weren't found in the underlying cursor
|
* @return the list of ids that weren't found in the underlying cursor
|
||||||
*/
|
*/
|
||||||
public ArrayList<Long> getMissingIds() {
|
public ArrayList<String> getMissingValues() {
|
||||||
return mMissingIds;
|
return mMissingValues;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public Collection<Long> getExtraIds() {
|
public Collection<String> getExtraValues() {
|
||||||
return mMapCursorPositions.keySet();
|
return mMapCursorPositions.keySet();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the extra object data that was passed in to be attached to the current row
|
|
||||||
*/
|
|
||||||
@Nullable
|
|
||||||
public Object getExtraData() {
|
|
||||||
int position = getPosition();
|
|
||||||
return position < mExtraData.size() ? mExtraData.get(position) : null;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void close() {
|
public void close() {
|
||||||
mCursor.close();
|
mCursor.close();
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,169 @@
|
||||||
|
/*
|
||||||
|
* Copyright (C) 2014 The CyanogenMod Project
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
package com.kabouzeid.gramophone.loader;
|
||||||
|
|
||||||
|
import android.database.AbstractCursor;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashMap;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This cursor basically wraps a song cursor and is given a list of the order of the ids of the
|
||||||
|
* contents of the cursor. It wraps the Cursor and simulates the internal cursor being sorted
|
||||||
|
* by moving the point to the appropriate spot
|
||||||
|
*/
|
||||||
|
public class SortedLongCursor extends AbstractCursor {
|
||||||
|
// cursor to wrap
|
||||||
|
private final Cursor mCursor;
|
||||||
|
// the map of external indices to internal indices
|
||||||
|
private ArrayList<Integer> mOrderedPositions;
|
||||||
|
// this contains the ids that weren't found in the underlying cursor
|
||||||
|
private ArrayList<Long> mMissingIds;
|
||||||
|
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||||
|
private HashMap<Long, Integer> mMapCursorPositions;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param cursor to wrap
|
||||||
|
* @param order the list of unique ids in sorted order to display
|
||||||
|
* @param columnName the column name of the id to look up in the internal cursor
|
||||||
|
*/
|
||||||
|
public SortedLongCursor(final Cursor cursor, final long[] order, final String columnName) {
|
||||||
|
|
||||||
|
mCursor = cursor;
|
||||||
|
mMissingIds = buildCursorPositionMapping(order, columnName);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This function populates mOrderedPositions with the cursor positions in the order based
|
||||||
|
* on the order passed in
|
||||||
|
*
|
||||||
|
* @param order the target order of the internal cursor
|
||||||
|
* @return returns the ids that aren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
private ArrayList<Long> buildCursorPositionMapping(@Nullable final long[] order, final String columnName) {
|
||||||
|
ArrayList<Long> missingIds = new ArrayList<>();
|
||||||
|
|
||||||
|
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||||
|
|
||||||
|
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||||
|
final int idPosition = mCursor.getColumnIndex(columnName);
|
||||||
|
|
||||||
|
if (mCursor.moveToFirst()) {
|
||||||
|
// first figure out where each of the ids are in the cursor
|
||||||
|
do {
|
||||||
|
mMapCursorPositions.put(mCursor.getLong(idPosition), mCursor.getPosition());
|
||||||
|
} while (mCursor.moveToNext());
|
||||||
|
|
||||||
|
// now create the ordered positions to map to the internal cursor given the
|
||||||
|
// external sort order
|
||||||
|
for (int i = 0; order != null && i < order.length; i++) {
|
||||||
|
final long id = order[i];
|
||||||
|
if (mMapCursorPositions.containsKey(id)) {
|
||||||
|
mOrderedPositions.add(mMapCursorPositions.get(id));
|
||||||
|
mMapCursorPositions.remove(id);
|
||||||
|
} else {
|
||||||
|
missingIds.add(id);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
mCursor.moveToFirst();
|
||||||
|
}
|
||||||
|
|
||||||
|
return missingIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that weren't found in the underlying cursor
|
||||||
|
*/
|
||||||
|
public ArrayList<Long> getMissingIds() {
|
||||||
|
return mMissingIds;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||||
|
*/
|
||||||
|
@NonNull
|
||||||
|
public Collection<Long> getExtraIds() {
|
||||||
|
return mMapCursorPositions.keySet();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void close() {
|
||||||
|
mCursor.close();
|
||||||
|
|
||||||
|
super.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getCount() {
|
||||||
|
return mOrderedPositions.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String[] getColumnNames() {
|
||||||
|
return mCursor.getColumnNames();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getString(int column) {
|
||||||
|
return mCursor.getString(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public short getShort(int column) {
|
||||||
|
return mCursor.getShort(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int getInt(int column) {
|
||||||
|
return mCursor.getInt(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getLong(int column) {
|
||||||
|
return mCursor.getLong(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public float getFloat(int column) {
|
||||||
|
return mCursor.getFloat(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public double getDouble(int column) {
|
||||||
|
return mCursor.getDouble(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean isNull(int column) {
|
||||||
|
return mCursor.isNull(column);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMove(int oldPosition, int newPosition) {
|
||||||
|
if (newPosition >= 0 && newPosition < getCount()) {
|
||||||
|
mCursor.moveToPosition(mOrderedPositions.get(newPosition));
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -43,7 +43,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
public static Cursor makeRecentTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||||
SortedCursor retCursor = makeRecentTracksCursorImpl(context);
|
SortedLongCursor retCursor = makeRecentTracksCursorImpl(context);
|
||||||
|
|
||||||
// clean up the databases with any ids not found
|
// clean up the databases with any ids not found
|
||||||
if (retCursor != null) {
|
if (retCursor != null) {
|
||||||
|
|
@ -59,7 +59,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
public static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
public static Cursor makeTopTracksCursorAndClearUpDatabase(@NonNull final Context context) {
|
||||||
SortedCursor retCursor = makeTopTracksCursorImpl(context);
|
SortedLongCursor retCursor = makeTopTracksCursorImpl(context);
|
||||||
|
|
||||||
// clean up the databases with any ids not found
|
// clean up the databases with any ids not found
|
||||||
if (retCursor != null) {
|
if (retCursor != null) {
|
||||||
|
|
@ -74,7 +74,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static SortedCursor makeRecentTracksCursorImpl(@NonNull final Context context) {
|
private static SortedLongCursor makeRecentTracksCursorImpl(@NonNull final Context context) {
|
||||||
// first get the top results ids from the internal database
|
// first get the top results ids from the internal database
|
||||||
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
||||||
|
|
||||||
|
|
@ -89,7 +89,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static SortedCursor makeTopTracksCursorImpl(@NonNull final Context context) {
|
private static SortedLongCursor makeTopTracksCursorImpl(@NonNull final Context context) {
|
||||||
// first get the top results ids from the internal database
|
// first get the top results ids from the internal database
|
||||||
Cursor songs = SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS);
|
Cursor songs = SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS);
|
||||||
|
|
||||||
|
|
@ -104,8 +104,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
private static SortedCursor makeSortedCursor(@NonNull final Context context, @Nullable final Cursor cursor,
|
private static SortedLongCursor makeSortedCursor(@NonNull final Context context, @Nullable final Cursor cursor, final int idColumn) {
|
||||||
final int idColumn) {
|
|
||||||
if (cursor != null && cursor.moveToFirst()) {
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
// create the list of ids to select against
|
// create the list of ids to select against
|
||||||
StringBuilder selection = new StringBuilder();
|
StringBuilder selection = new StringBuilder();
|
||||||
|
|
@ -133,7 +132,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
||||||
Cursor songCursor = SongLoader.makeSongCursor(context, selection.toString(), null);
|
Cursor songCursor = SongLoader.makeSongCursor(context, selection.toString(), null);
|
||||||
if (songCursor != null) {
|
if (songCursor != null) {
|
||||||
// now return the wrapped TopTracksCursor to handle sorting given order
|
// now return the wrapped TopTracksCursor to handle sorting given order
|
||||||
return new SortedCursor(songCursor, order, BaseColumns._ID, null);
|
return new SortedLongCursor(songCursor, order, BaseColumns._ID);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -66,6 +66,7 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
||||||
@Bind(R.id.drawer_layout)
|
@Bind(R.id.drawer_layout)
|
||||||
DrawerLayout drawerLayout;
|
DrawerLayout drawerLayout;
|
||||||
|
|
||||||
|
@Nullable
|
||||||
MainActivityFragmentCallbacks currentFragment;
|
MainActivityFragmentCallbacks currentFragment;
|
||||||
|
|
||||||
@Nullable
|
@Nullable
|
||||||
|
|
@ -88,7 +89,11 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
||||||
|
|
||||||
setUpDrawerLayout();
|
setUpDrawerLayout();
|
||||||
|
|
||||||
setMusicChooser(PreferenceUtil.getInstance(this).getLastMusicChooser());
|
if (savedInstanceState == null) {
|
||||||
|
setMusicChooser(PreferenceUtil.getInstance(this).getLastMusicChooser());
|
||||||
|
} else {
|
||||||
|
restoreCurrentFragment();
|
||||||
|
}
|
||||||
|
|
||||||
if (!checkShowIntro()) {
|
if (!checkShowIntro()) {
|
||||||
checkShowChangelog();
|
checkShowChangelog();
|
||||||
|
|
@ -100,20 +105,24 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
||||||
switch (key) {
|
switch (key) {
|
||||||
case LIBRARY:
|
case LIBRARY:
|
||||||
navigationView.setCheckedItem(R.id.nav_library);
|
navigationView.setCheckedItem(R.id.nav_library);
|
||||||
setCurrentFragment(new LibraryFragment());
|
setCurrentFragment(LibraryFragment.newInstance());
|
||||||
break;
|
break;
|
||||||
case FOLDERS:
|
case FOLDERS:
|
||||||
navigationView.setCheckedItem(R.id.nav_folders);
|
navigationView.setCheckedItem(R.id.nav_folders);
|
||||||
setCurrentFragment(new FolderFragment());
|
setCurrentFragment(FolderFragment.newInstance());
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setCurrentFragment(Fragment fragment) {
|
private void setCurrentFragment(@SuppressWarnings("NullableProblems") Fragment fragment) {
|
||||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, LibraryFragment.TAG).commit();
|
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, LibraryFragment.TAG).commit();
|
||||||
currentFragment = (MainActivityFragmentCallbacks) fragment;
|
currentFragment = (MainActivityFragmentCallbacks) fragment;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void restoreCurrentFragment() {
|
||||||
|
currentFragment = (MainActivityFragmentCallbacks) getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||||
super.onActivityResult(requestCode, resultCode, data);
|
super.onActivityResult(requestCode, resultCode, data);
|
||||||
|
|
@ -254,12 +263,13 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public boolean handleBackPress() {
|
||||||
if (drawerLayout.isDrawerOpen(navigationView)) drawerLayout.closeDrawers();
|
if (drawerLayout.isDrawerOpen(navigationView)) {
|
||||||
else if (currentFragment == null || !currentFragment.onBackPressed())
|
drawerLayout.closeDrawers();
|
||||||
super.onBackPressed();
|
return true;
|
||||||
|
}
|
||||||
|
return super.handleBackPress() || (currentFragment != null && currentFragment.handleBackPress());
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handlePlaybackIntent(@Nullable Intent intent) {
|
private void handlePlaybackIntent(@Nullable Intent intent) {
|
||||||
|
|
@ -391,6 +401,6 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface MainActivityFragmentCallbacks {
|
public interface MainActivityFragmentCallbacks {
|
||||||
boolean onBackPressed();
|
boolean handleBackPress();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -173,13 +173,18 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onBackPressed() {
|
public void onBackPressed() {
|
||||||
|
if (!handleBackPress())
|
||||||
|
super.onBackPressed();
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean handleBackPress() {
|
||||||
if (slidingUpPanelLayout.getPanelHeight() != 0 && playerFragment.onBackPressed())
|
if (slidingUpPanelLayout.getPanelHeight() != 0 && playerFragment.onBackPressed())
|
||||||
return;
|
return true;
|
||||||
if (getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
if (getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||||
collapsePanel();
|
collapsePanel();
|
||||||
return;
|
return true;
|
||||||
}
|
}
|
||||||
super.onBackPressed();
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|
|
||||||
|
|
@ -1,24 +1,141 @@
|
||||||
package com.kabouzeid.gramophone.ui.fragments;
|
package com.kabouzeid.gramophone.ui.fragments;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
import android.os.Environment;
|
||||||
|
import android.provider.MediaStore.Audio.AudioColumns;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
import android.support.design.widget.AppBarLayout;
|
||||||
|
import android.support.v7.widget.LinearLayoutManager;
|
||||||
|
import android.support.v7.widget.RecyclerView;
|
||||||
|
import android.support.v7.widget.Toolbar;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.Menu;
|
||||||
|
import android.view.MenuInflater;
|
||||||
|
import android.view.MenuItem;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.Toast;
|
||||||
|
|
||||||
|
import com.afollestad.materialcab.MaterialCab;
|
||||||
|
import com.kabouzeid.appthemehelper.ThemeStore;
|
||||||
|
import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper;
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
|
import com.kabouzeid.gramophone.adapter.SongFileAdapter;
|
||||||
|
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
|
||||||
|
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
|
||||||
|
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
|
||||||
|
import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
|
import com.kabouzeid.gramophone.loader.SongLoader;
|
||||||
|
import com.kabouzeid.gramophone.loader.SortedCursor;
|
||||||
|
import com.kabouzeid.gramophone.model.Song;
|
||||||
import com.kabouzeid.gramophone.ui.activities.MainActivity;
|
import com.kabouzeid.gramophone.ui.activities.MainActivity;
|
||||||
|
import com.kabouzeid.gramophone.util.PhonographColorUtil;
|
||||||
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||||
|
import com.kabouzeid.gramophone.util.ViewUtil;
|
||||||
|
import com.kabouzeid.gramophone.views.BreadCrumbLayout;
|
||||||
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileFilter;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import butterknife.Bind;
|
||||||
import butterknife.ButterKnife;
|
import butterknife.ButterKnife;
|
||||||
|
import hugo.weaving.DebugLog;
|
||||||
|
|
||||||
public class FolderFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks {
|
public class FolderFragment extends AbsMainActivityFragment implements MainActivity.MainActivityFragmentCallbacks, CabHolder, BreadCrumbLayout.SelectionCallback, SongFileAdapter.Callbacks, AppBarLayout.OnOffsetChangedListener {
|
||||||
public static final String TAG = FolderFragment.class.getSimpleName();
|
public static final String TAG = FolderFragment.class.getSimpleName();
|
||||||
|
|
||||||
|
protected static final String PATH = "path";
|
||||||
|
protected static final String CRUMBS = "crumbs";
|
||||||
|
|
||||||
|
@Bind(R.id.container)
|
||||||
|
View container;
|
||||||
|
@Bind(android.R.id.empty)
|
||||||
|
View empty;
|
||||||
|
@Bind(R.id.toolbar)
|
||||||
|
Toolbar toolbar;
|
||||||
|
@Bind(R.id.bread_crumbs)
|
||||||
|
BreadCrumbLayout breadCrumbs;
|
||||||
|
@Bind(R.id.appbar)
|
||||||
|
AppBarLayout appbar;
|
||||||
|
@Bind(R.id.recycler_view)
|
||||||
|
FastScrollRecyclerView recyclerView;
|
||||||
|
|
||||||
|
private SongFileAdapter adapter;
|
||||||
|
private MaterialCab cab;
|
||||||
|
|
||||||
public FolderFragment() {
|
public FolderFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static FolderFragment newInstance() {
|
||||||
|
return newInstance(getDefaultStartFolder());
|
||||||
|
}
|
||||||
|
|
||||||
|
public static FolderFragment newInstance(File directory) {
|
||||||
|
FolderFragment frag = new FolderFragment();
|
||||||
|
Bundle b = new Bundle();
|
||||||
|
b.putSerializable(PATH, directory);
|
||||||
|
frag.setArguments(b);
|
||||||
|
return frag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCrumb(BreadCrumbLayout.Crumb crumb, boolean addToHistory) {
|
||||||
|
saveScrollPosition();
|
||||||
|
updateAdapter(crumb.getFile());
|
||||||
|
recyclerView.getLayoutManager().scrollToPosition(crumb.getScrollPosition());
|
||||||
|
breadCrumbs.setActiveOrAdd(crumb, false);
|
||||||
|
if (addToHistory)
|
||||||
|
breadCrumbs.addHistory(crumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void saveScrollPosition() {
|
||||||
|
if (breadCrumbs.size() > 0) {
|
||||||
|
BreadCrumbLayout.Crumb crumb = breadCrumbs.getCrumb(breadCrumbs.getActiveIndex());
|
||||||
|
crumb.setScrollPosition(((LinearLayoutManager) recyclerView.getLayoutManager()).findFirstVisibleItemPosition());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSaveInstanceState(Bundle outState) {
|
||||||
|
super.onSaveInstanceState(outState);
|
||||||
|
outState.putParcelable(CRUMBS, breadCrumbs.getStateWrapper());
|
||||||
|
}
|
||||||
|
|
||||||
|
private void updateAdapter(File directory) {
|
||||||
|
ArrayList<File> files = loadFiles(directory);
|
||||||
|
if (adapter == null) {
|
||||||
|
adapter = new SongFileAdapter(getMainActivity(), files, R.layout.item_list, this, this);
|
||||||
|
adapter.registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
|
||||||
|
@Override
|
||||||
|
public void onChanged() {
|
||||||
|
super.onChanged();
|
||||||
|
checkIsEmpty();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
recyclerView.setAdapter(adapter);
|
||||||
|
checkIsEmpty();
|
||||||
|
} else {
|
||||||
|
adapter.swapDataSet(files);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
private ArrayList<File> loadFiles(File directory) {
|
||||||
|
ArrayList<File> fileList = new ArrayList<>();
|
||||||
|
File[] files = directory.listFiles();
|
||||||
|
if (files != null) {
|
||||||
|
Collections.addAll(fileList, files);
|
||||||
|
}
|
||||||
|
return fileList;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||||
|
|
@ -30,16 +147,249 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
|
||||||
@Override
|
@Override
|
||||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||||
PreferenceUtil.getInstance(getActivity()).setLastPage(-2);
|
PreferenceUtil.getInstance(getActivity()).setLastPage(-2);
|
||||||
|
|
||||||
|
getMainActivity().setStatusbarColorAuto();
|
||||||
|
getMainActivity().setNavigationbarColorAuto();
|
||||||
|
getMainActivity().setTaskDescriptionColorAuto();
|
||||||
|
|
||||||
|
setUpAppbarColor();
|
||||||
|
setUpToolbar();
|
||||||
|
setUpBreadCrumbs();
|
||||||
|
setUpRecyclerView();
|
||||||
|
|
||||||
|
if (savedInstanceState == null) {
|
||||||
|
setCrumb(new BreadCrumbLayout.Crumb((File) getArguments().getSerializable(PATH)), true);
|
||||||
|
} else {
|
||||||
|
breadCrumbs.restoreFromStateWrapper((BreadCrumbLayout.SavedStateWrapper) savedInstanceState.getParcelable(CRUMBS));
|
||||||
|
setCrumb(breadCrumbs.getCrumb(breadCrumbs.getActiveIndex()), true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpAppbarColor() {
|
||||||
|
int primaryColor = ThemeStore.primaryColor(getActivity());
|
||||||
|
appbar.setBackgroundColor(primaryColor);
|
||||||
|
toolbar.setBackgroundColor(primaryColor);
|
||||||
|
breadCrumbs.setBackgroundColor(primaryColor);
|
||||||
|
breadCrumbs.setActivatedContentColor(ToolbarContentTintHelper.toolbarTitleColor(getActivity(), primaryColor));
|
||||||
|
breadCrumbs.setDeactivatedContentColor(ToolbarContentTintHelper.toolbarSubtitleColor(getActivity(), primaryColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpToolbar() {
|
||||||
|
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
|
||||||
|
getActivity().setTitle(R.string.app_name);
|
||||||
|
getMainActivity().setSupportActionBar(toolbar);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpBreadCrumbs() {
|
||||||
|
breadCrumbs.setCallback(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setUpRecyclerView() {
|
||||||
|
ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), recyclerView, ThemeStore.accentColor(getActivity()));
|
||||||
|
|
||||||
|
recyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));
|
||||||
|
|
||||||
|
appbar.addOnOffsetChangedListener(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause() {
|
||||||
|
super.onPause();
|
||||||
|
saveScrollPosition();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onDestroyView() {
|
public void onDestroyView() {
|
||||||
|
appbar.removeOnOffsetChangedListener(this);
|
||||||
ButterKnife.unbind(this);
|
ButterKnife.unbind(this);
|
||||||
super.onDestroyView();
|
super.onDestroyView();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBackPressed() {
|
public boolean handleBackPress() {
|
||||||
|
if (cab != null && cab.isActive()) {
|
||||||
|
cab.finish();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
if (breadCrumbs.popHistory()) {
|
||||||
|
setCrumb(breadCrumbs.lastHistory(), false);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
@Override
|
||||||
|
public MaterialCab openCab(int menuRes, MaterialCab.Callback callback) {
|
||||||
|
if (cab != null && cab.isActive()) cab.finish();
|
||||||
|
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
|
||||||
|
.setMenu(menuRes)
|
||||||
|
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||||
|
.setBackgroundColor(PhonographColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
|
||||||
|
.start(callback);
|
||||||
|
return cab;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
|
||||||
|
super.onCreateOptionsMenu(menu, inflater);
|
||||||
|
inflater.inflate(R.menu.menu_folders, menu);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
|
switch (item.getItemId()) {
|
||||||
|
case R.id.action_go_to_standard_folder:
|
||||||
|
setCrumb(new BreadCrumbLayout.Crumb(getDefaultStartFolder()), true);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
return super.onOptionsItemSelected(item);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onCrumbSelection(BreadCrumbLayout.Crumb crumb, int index) {
|
||||||
|
setCrumb(crumb, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static File getDefaultStartFolder() {
|
||||||
|
File externalStorageDir = Environment.getExternalStorageDirectory();
|
||||||
|
File musicFolder = new File(externalStorageDir, "Music");
|
||||||
|
File startFolder;
|
||||||
|
if (musicFolder.exists() && musicFolder.isDirectory()) {
|
||||||
|
startFolder = musicFolder;
|
||||||
|
} else if (externalStorageDir.exists() && externalStorageDir.isDirectory()) {
|
||||||
|
startFolder = externalStorageDir;
|
||||||
|
} else {
|
||||||
|
startFolder = new File("/"); // root
|
||||||
|
}
|
||||||
|
return startFolder;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileSelected(File file) {
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
setCrumb(new BreadCrumbLayout.Crumb(file), true);
|
||||||
|
} else {
|
||||||
|
ArrayList<Song> songs = getAllSongs(file.getParentFile(), new FileFilter() {
|
||||||
|
@Override
|
||||||
|
public boolean accept(File pathname) {
|
||||||
|
return !pathname.isDirectory();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
int startIndex = -1;
|
||||||
|
for (int i = 0; i < songs.size(); i++) {
|
||||||
|
if (file.getPath().equals(songs.get(i).data)) {
|
||||||
|
startIndex = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (startIndex > -1) {
|
||||||
|
MusicPlayerRemote.openQueue(songs, startIndex, true);
|
||||||
|
} else {
|
||||||
|
Toast.makeText(getActivity(), "Could not play selected file.", Toast.LENGTH_SHORT).show(); // TODO replace with proper text.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static SortedCursor makeSongCursor(@NonNull final Context context, @Nullable final List<File> files) {
|
||||||
|
String selection = null;
|
||||||
|
String[] values = null;
|
||||||
|
|
||||||
|
if (files != null && files.size() > 0 && files.size() < 999) { // 999 is the max amount Androids SQL implementation can handle.
|
||||||
|
selection = AudioColumns.DATA + " IN (" + makePlaceholders(files.size()) + ")";
|
||||||
|
|
||||||
|
values = new String[files.size()];
|
||||||
|
for (int i = 0; i < files.size(); i++) {
|
||||||
|
values[i] = files.get(i).getPath();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Cursor songCursor = SongLoader.makeSongCursor(context, selection, values);
|
||||||
|
|
||||||
|
return songCursor == null ? null : new SortedCursor(songCursor, values, AudioColumns.DATA);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static String makePlaceholders(int len) {
|
||||||
|
StringBuilder sb = new StringBuilder(len * 2 - 1);
|
||||||
|
sb.append("?");
|
||||||
|
for (int i = 1; i < len; i++) {
|
||||||
|
sb.append(",?");
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFileMenuClicked(File file) {
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddToPlaylist(ArrayList<File> files) {
|
||||||
|
AddToPlaylistDialog.create(getAllSongs(files, null)).show(getFragmentManager(), "ADD_PLAYLIST");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAddToCurrentPlaying(ArrayList<File> files) {
|
||||||
|
MusicPlayerRemote.enqueue(getAllSongs(files, null));
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onDeleteFromDevice(ArrayList<File> files) {
|
||||||
|
DeleteSongsDialog.create(getAllSongs(files, null)).show(getFragmentManager(), "DELETE_SONGS");
|
||||||
|
}
|
||||||
|
|
||||||
|
private ArrayList<Song> getAllSongs(File dir, @Nullable FileFilter fileFilter) {
|
||||||
|
ArrayList<Song> songs = new ArrayList<>();
|
||||||
|
|
||||||
|
ArrayList<File> files = new ArrayList<>();
|
||||||
|
File[] fileArray = dir.listFiles(fileFilter);
|
||||||
|
if (fileArray != null) Collections.addAll(files, fileArray);
|
||||||
|
Iterator<File> iterator = files.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
File file = iterator.next();
|
||||||
|
if (file.isDirectory()) {
|
||||||
|
songs.addAll(getAllSongs(file, fileFilter));
|
||||||
|
iterator.remove();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortedCursor cursor = makeSongCursor(getActivity(), files);
|
||||||
|
songs.addAll(SongLoader.getSongs(cursor));
|
||||||
|
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@DebugLog
|
||||||
|
private ArrayList<Song> getAllSongs(List<File> files, @Nullable FileFilter fileFilter) {
|
||||||
|
ArrayList<Song> songs = new ArrayList<>();
|
||||||
|
|
||||||
|
Iterator<File> iterator = files.iterator();
|
||||||
|
while (iterator.hasNext()) {
|
||||||
|
File file = iterator.next();
|
||||||
|
if (fileFilter != null && !fileFilter.accept(file)) {
|
||||||
|
iterator.remove();
|
||||||
|
} else if (file.isDirectory()) {
|
||||||
|
iterator.remove();
|
||||||
|
songs.addAll(getAllSongs(file, fileFilter));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
SortedCursor cursor = makeSongCursor(getActivity(), files);
|
||||||
|
songs.addAll(SongLoader.getSongs(cursor));
|
||||||
|
|
||||||
|
return songs;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onOffsetChanged(AppBarLayout appBarLayout, int verticalOffset) {
|
||||||
|
container.setPadding(container.getPaddingLeft(), container.getPaddingTop(), container.getPaddingRight(), appbar.getTotalScrollRange() + verticalOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void checkIsEmpty() {
|
||||||
|
if (empty != null) {
|
||||||
|
empty.setVisibility(adapter == null || adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -31,7 +31,7 @@ import com.kabouzeid.gramophone.interfaces.CabHolder;
|
||||||
import com.kabouzeid.gramophone.loader.SongLoader;
|
import com.kabouzeid.gramophone.loader.SongLoader;
|
||||||
import com.kabouzeid.gramophone.ui.activities.MainActivity;
|
import com.kabouzeid.gramophone.ui.activities.MainActivity;
|
||||||
import com.kabouzeid.gramophone.ui.activities.SearchActivity;
|
import com.kabouzeid.gramophone.ui.activities.SearchActivity;
|
||||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AbsLibraryRecyclerViewCustomGridSizePagerFragment;
|
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AbsLibraryPagerRecyclerViewCustomGridSizeFragment;
|
||||||
import com.kabouzeid.gramophone.util.NavigationUtil;
|
import com.kabouzeid.gramophone.util.NavigationUtil;
|
||||||
import com.kabouzeid.gramophone.util.PhonographColorUtil;
|
import com.kabouzeid.gramophone.util.PhonographColorUtil;
|
||||||
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||||
|
|
@ -55,6 +55,10 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
private MusicLibraryPagerAdapter pagerAdapter;
|
private MusicLibraryPagerAdapter pagerAdapter;
|
||||||
private MaterialCab cab;
|
private MaterialCab cab;
|
||||||
|
|
||||||
|
public static LibraryFragment newInstance() {
|
||||||
|
return new LibraryFragment();
|
||||||
|
}
|
||||||
|
|
||||||
public LibraryFragment() {
|
public LibraryFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -87,7 +91,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
appbar.setBackgroundColor(primaryColor);
|
appbar.setBackgroundColor(primaryColor);
|
||||||
toolbar.setBackgroundColor(primaryColor);
|
toolbar.setBackgroundColor(primaryColor);
|
||||||
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
|
toolbar.setNavigationIcon(R.drawable.ic_menu_white_24dp);
|
||||||
getActivity().setTitle(getResources().getString(R.string.app_name));
|
getActivity().setTitle(R.string.app_name);
|
||||||
getMainActivity().setSupportActionBar(toolbar);
|
getMainActivity().setSupportActionBar(toolbar);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -120,11 +124,12 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
return pager.getCurrentItem() == MusicLibraryPagerAdapter.MusicFragments.PLAYLIST.ordinal();
|
return pager.getCurrentItem() == MusicLibraryPagerAdapter.MusicFragments.PLAYLIST.ordinal();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
public MaterialCab openCab(final int menu, final MaterialCab.Callback callback) {
|
public MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback) {
|
||||||
if (cab != null && cab.isActive()) cab.finish();
|
if (cab != null && cab.isActive()) cab.finish();
|
||||||
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
|
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
|
||||||
.setMenu(menu)
|
.setMenu(menuRes)
|
||||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||||
.setBackgroundColor(PhonographColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
|
.setBackgroundColor(PhonographColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
|
||||||
.start(callback);
|
.start(callback);
|
||||||
|
|
@ -151,8 +156,8 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title);
|
menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title);
|
||||||
}
|
}
|
||||||
Fragment currentFragment = getCurrentFragment();
|
Fragment currentFragment = getCurrentFragment();
|
||||||
if (currentFragment instanceof AbsLibraryRecyclerViewCustomGridSizePagerFragment && currentFragment.isAdded()) {
|
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment && currentFragment.isAdded()) {
|
||||||
AbsLibraryRecyclerViewCustomGridSizePagerFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryRecyclerViewCustomGridSizePagerFragment) currentFragment;
|
AbsLibraryPagerRecyclerViewCustomGridSizeFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
|
||||||
|
|
||||||
MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size);
|
MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size);
|
||||||
if (Util.isLandscape(getResources())) {
|
if (Util.isLandscape(getResources())) {
|
||||||
|
|
@ -171,8 +176,8 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
@Override
|
@Override
|
||||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||||
Fragment currentFragment = getCurrentFragment();
|
Fragment currentFragment = getCurrentFragment();
|
||||||
if (currentFragment instanceof AbsLibraryRecyclerViewCustomGridSizePagerFragment) {
|
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) {
|
||||||
AbsLibraryRecyclerViewCustomGridSizePagerFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryRecyclerViewCustomGridSizePagerFragment) currentFragment;
|
AbsLibraryPagerRecyclerViewCustomGridSizeFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
|
||||||
if (item.getItemId() == R.id.action_colored_footers) {
|
if (item.getItemId() == R.id.action_colored_footers) {
|
||||||
item.setChecked(!item.isChecked());
|
item.setChecked(!item.isChecked());
|
||||||
absLibraryRecyclerViewCustomGridSizeFragment.setAndSaveUsePalette(item.isChecked());
|
absLibraryRecyclerViewCustomGridSizeFragment.setAndSaveUsePalette(item.isChecked());
|
||||||
|
|
@ -204,7 +209,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
return super.onOptionsItemSelected(item);
|
return super.onOptionsItemSelected(item);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setUpGridSizeMenu(@NonNull AbsLibraryRecyclerViewCustomGridSizePagerFragment fragment, @NonNull SubMenu gridSizeMenu) {
|
private void setUpGridSizeMenu(@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull SubMenu gridSizeMenu) {
|
||||||
switch (fragment.getGridSize()) {
|
switch (fragment.getGridSize()) {
|
||||||
case 1:
|
case 1:
|
||||||
gridSizeMenu.findItem(R.id.action_grid_size_1).setChecked(true);
|
gridSizeMenu.findItem(R.id.action_grid_size_1).setChecked(true);
|
||||||
|
|
@ -252,7 +257,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean handleGridSizeMenuItem(@NonNull AbsLibraryRecyclerViewCustomGridSizePagerFragment fragment, @NonNull MenuItem item) {
|
private boolean handleGridSizeMenuItem(@NonNull AbsLibraryPagerRecyclerViewCustomGridSizeFragment fragment, @NonNull MenuItem item) {
|
||||||
int gridSize = 0;
|
int gridSize = 0;
|
||||||
switch (item.getItemId()) {
|
switch (item.getItemId()) {
|
||||||
case R.id.action_grid_size_1:
|
case R.id.action_grid_size_1:
|
||||||
|
|
@ -290,7 +295,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean onBackPressed() {
|
public boolean handleBackPress() {
|
||||||
if (cab != null && cab.isActive()) {
|
if (cab != null && cab.isActive()) {
|
||||||
cab.finish();
|
cab.finish();
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ import com.kabouzeid.gramophone.util.Util;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public abstract class AbsLibraryRecyclerViewCustomGridSizePagerFragment<A extends RecyclerView.Adapter, LM extends RecyclerView.LayoutManager> extends AbsLibraryRecyclerViewPagerFragment<A, LM> {
|
public abstract class AbsLibraryPagerRecyclerViewCustomGridSizeFragment<A extends RecyclerView.Adapter, LM extends RecyclerView.LayoutManager> extends AbsLibraryPagerRecyclerViewFragment<A, LM> {
|
||||||
private int gridSize;
|
private int gridSize;
|
||||||
|
|
||||||
private boolean usePaletteInitialized;
|
private boolean usePaletteInitialized;
|
||||||
|
|
@ -14,11 +14,9 @@ import android.view.ViewGroup;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import com.kabouzeid.appthemehelper.ThemeStore;
|
import com.kabouzeid.appthemehelper.ThemeStore;
|
||||||
import com.kabouzeid.appthemehelper.util.ATHUtil;
|
|
||||||
import com.kabouzeid.appthemehelper.util.ColorUtil;
|
|
||||||
import com.kabouzeid.appthemehelper.util.MaterialValueHelper;
|
|
||||||
import com.kabouzeid.gramophone.R;
|
import com.kabouzeid.gramophone.R;
|
||||||
import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener;
|
import com.kabouzeid.gramophone.interfaces.MusicServiceEventListener;
|
||||||
|
import com.kabouzeid.gramophone.util.ViewUtil;
|
||||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||||
|
|
||||||
import butterknife.Bind;
|
import butterknife.Bind;
|
||||||
|
|
@ -27,9 +25,9 @@ import butterknife.ButterKnife;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public abstract class AbsLibraryRecyclerViewPagerFragment<A extends RecyclerView.Adapter, LM extends RecyclerView.LayoutManager> extends AbsLibraryPagerFragment implements OnOffsetChangedListener, MusicServiceEventListener {
|
public abstract class AbsLibraryPagerRecyclerViewFragment<A extends RecyclerView.Adapter, LM extends RecyclerView.LayoutManager> extends AbsLibraryPagerFragment implements OnOffsetChangedListener, MusicServiceEventListener {
|
||||||
|
|
||||||
public static final String TAG = AbsLibraryRecyclerViewPagerFragment.class.getSimpleName();
|
public static final String TAG = AbsLibraryPagerRecyclerViewFragment.class.getSimpleName();
|
||||||
|
|
||||||
@Bind(R.id.container)
|
@Bind(R.id.container)
|
||||||
View container;
|
View container;
|
||||||
|
|
@ -63,11 +61,7 @@ public abstract class AbsLibraryRecyclerViewPagerFragment<A extends RecyclerView
|
||||||
|
|
||||||
private void setUpRecyclerView() {
|
private void setUpRecyclerView() {
|
||||||
if (recyclerView instanceof FastScrollRecyclerView) {
|
if (recyclerView instanceof FastScrollRecyclerView) {
|
||||||
int accentColor = ThemeStore.accentColor(getActivity());
|
ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(getActivity()));
|
||||||
((FastScrollRecyclerView) recyclerView).setPopupBgColor(accentColor);
|
|
||||||
((FastScrollRecyclerView) recyclerView).setPopupTextColor(MaterialValueHelper.getPrimaryTextColor(getActivity(), ColorUtil.isColorLight(accentColor)));
|
|
||||||
((FastScrollRecyclerView) recyclerView).setThumbColor(accentColor);
|
|
||||||
((FastScrollRecyclerView) recyclerView).setTrackColor(ColorUtil.withAlpha(ATHUtil.resolveColor(getContext(), R.attr.colorControlNormal), 0.12f));
|
|
||||||
}
|
}
|
||||||
invalidateLayoutManager();
|
invalidateLayoutManager();
|
||||||
invalidateAdapter();
|
invalidateAdapter();
|
||||||
|
|
@ -11,8 +11,8 @@ import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public class AlbumsPagerFragment extends AbsLibraryRecyclerViewCustomGridSizePagerFragment<AlbumAdapter, GridLayoutManager> {
|
public class AlbumsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager> {
|
||||||
public static final String TAG = AlbumsPagerFragment.class.getSimpleName();
|
public static final String TAG = AlbumsFragment.class.getSimpleName();
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected GridLayoutManager createLayoutManager() {
|
protected GridLayoutManager createLayoutManager() {
|
||||||
|
|
@ -11,9 +11,9 @@ import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public class ArtistsPagerFragment extends AbsLibraryRecyclerViewCustomGridSizePagerFragment<ArtistAdapter, GridLayoutManager> {
|
public class ArtistsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment<ArtistAdapter, GridLayoutManager> {
|
||||||
|
|
||||||
public static final String TAG = ArtistsPagerFragment.class.getSimpleName();
|
public static final String TAG = ArtistsFragment.class.getSimpleName();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -16,9 +16,9 @@ import java.util.ArrayList;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public class PlaylistsPagerFragment extends AbsLibraryRecyclerViewPagerFragment<PlaylistAdapter, LinearLayoutManager> {
|
public class PlaylistsFragment extends AbsLibraryPagerRecyclerViewFragment<PlaylistAdapter, LinearLayoutManager> {
|
||||||
|
|
||||||
public static final String TAG = PlaylistsPagerFragment.class.getSimpleName();
|
public static final String TAG = PlaylistsFragment.class.getSimpleName();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -15,9 +15,9 @@ import java.util.ArrayList;
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
public class SongsPagerFragment extends AbsLibraryRecyclerViewCustomGridSizePagerFragment<SongAdapter, GridLayoutManager> {
|
public class SongsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment<SongAdapter, GridLayoutManager> {
|
||||||
|
|
||||||
public static final String TAG = SongsPagerFragment.class.getSimpleName();
|
public static final String TAG = SongsFragment.class.getSimpleName();
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
@Override
|
@Override
|
||||||
|
|
@ -13,6 +13,7 @@ import android.net.ConnectivityManager;
|
||||||
import android.net.NetworkInfo;
|
import android.net.NetworkInfo;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.support.annotation.AttrRes;
|
import android.support.annotation.AttrRes;
|
||||||
|
import android.support.annotation.ColorInt;
|
||||||
import android.support.annotation.DrawableRes;
|
import android.support.annotation.DrawableRes;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
|
|
@ -94,7 +95,7 @@ public class Util {
|
||||||
return dimensionPixelSize;
|
return dimensionPixelSize;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableResId, int color) {
|
public static Drawable getTintedDrawable(@NonNull Context context, @DrawableRes int drawableResId, @ColorInt int color) {
|
||||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.LOLLIPOP) {
|
||||||
// vector support
|
// vector support
|
||||||
context = TintContextWrapper.wrap(context);
|
context = TintContextWrapper.wrap(context);
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,12 @@ import android.view.View;
|
||||||
import android.view.animation.PathInterpolator;
|
import android.view.animation.PathInterpolator;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.kabouzeid.appthemehelper.util.ATHUtil;
|
||||||
|
import com.kabouzeid.appthemehelper.util.ColorUtil;
|
||||||
|
import com.kabouzeid.appthemehelper.util.MaterialValueHelper;
|
||||||
|
import com.kabouzeid.gramophone.R;
|
||||||
|
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @author Karim Abou Zeid (kabouzeid)
|
* @author Karim Abou Zeid (kabouzeid)
|
||||||
*/
|
*/
|
||||||
|
|
@ -71,4 +77,11 @@ public class ViewUtil {
|
||||||
|
|
||||||
return (x >= left) && (x <= right) && (y >= top) && (y <= bottom);
|
return (x >= left) && (x <= right) && (y >= top) && (y <= bottom);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static void setUpFastScrollRecyclerViewColor(Context context, FastScrollRecyclerView recyclerView, int accentColor) {
|
||||||
|
recyclerView.setPopupBgColor(accentColor);
|
||||||
|
recyclerView.setPopupTextColor(MaterialValueHelper.getPrimaryTextColor(context, ColorUtil.isColorLight(accentColor)));
|
||||||
|
recyclerView.setThumbColor(accentColor);
|
||||||
|
recyclerView.setTrackColor(ColorUtil.withAlpha(ATHUtil.resolveColor(context, R.attr.colorControlNormal), 0.12f));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -0,0 +1,407 @@
|
||||||
|
package com.kabouzeid.gramophone.views;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.PorterDuff;
|
||||||
|
import android.os.Build;
|
||||||
|
import android.os.Parcel;
|
||||||
|
import android.os.Parcelable;
|
||||||
|
import android.support.annotation.ColorInt;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.util.AttributeSet;
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.HorizontalScrollView;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
import android.widget.LinearLayout;
|
||||||
|
import android.widget.TextView;
|
||||||
|
|
||||||
|
import com.kabouzeid.appthemehelper.ThemeStore;
|
||||||
|
import com.kabouzeid.gramophone.R;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.Iterator;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Aidan Follestad (afollestad), modified for Phonograph by Karim Abou Zeid (kabouzeid)
|
||||||
|
*/
|
||||||
|
public class BreadCrumbLayout extends HorizontalScrollView implements View.OnClickListener {
|
||||||
|
|
||||||
|
@ColorInt
|
||||||
|
private int contentColorActivated;
|
||||||
|
@ColorInt
|
||||||
|
private int contentColorDeactivated;
|
||||||
|
|
||||||
|
public static class Crumb implements Parcelable {
|
||||||
|
|
||||||
|
public Crumb(File file) {
|
||||||
|
mFile = file;
|
||||||
|
}
|
||||||
|
|
||||||
|
private final File mFile;
|
||||||
|
private int mScrollPos;
|
||||||
|
|
||||||
|
public int getScrollPosition() {
|
||||||
|
return mScrollPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setScrollPosition(int scrollY) {
|
||||||
|
this.mScrollPos = scrollY;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return mFile.getPath().equals("/") ? "root" : mFile.getName();
|
||||||
|
}
|
||||||
|
|
||||||
|
public File getFile() {
|
||||||
|
return mFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean equals(Object o) {
|
||||||
|
return (o instanceof Crumb) && ((Crumb) o).getFile() != null &&
|
||||||
|
((Crumb) o).getFile().equals(getFile());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeSerializable(this.mFile);
|
||||||
|
dest.writeInt(this.mScrollPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected Crumb(Parcel in) {
|
||||||
|
this.mFile = (File) in.readSerializable();
|
||||||
|
this.mScrollPos = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<Crumb> CREATOR = new Creator<Crumb>() {
|
||||||
|
public Crumb createFromParcel(Parcel source) {
|
||||||
|
return new Crumb(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crumb[] newArray(int size) {
|
||||||
|
return new Crumb[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public interface SelectionCallback {
|
||||||
|
void onCrumbSelection(Crumb crumb, int index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreadCrumbLayout(Context context) {
|
||||||
|
super(context);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreadCrumbLayout(Context context, AttributeSet attrs) {
|
||||||
|
super(context, attrs);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
public BreadCrumbLayout(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||||
|
super(context, attrs, defStyleAttr);
|
||||||
|
init();
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stores currently visible crumbs
|
||||||
|
private List<Crumb> mCrumbs;
|
||||||
|
// Used in setActiveOrAdd() between clearing crumbs and adding the new set, nullified afterwards
|
||||||
|
private List<Crumb> mOldCrumbs;
|
||||||
|
// Stores user's navigation history, like a fragment back stack
|
||||||
|
private List<Crumb> mHistory;
|
||||||
|
|
||||||
|
private LinearLayout mChildFrame;
|
||||||
|
private int mActive;
|
||||||
|
private SelectionCallback mCallback;
|
||||||
|
|
||||||
|
private void init() {
|
||||||
|
contentColorActivated = ThemeStore.textColorPrimary(getContext());
|
||||||
|
contentColorDeactivated = ThemeStore.textColorSecondary(getContext());
|
||||||
|
setMinimumHeight((int) getResources().getDimension(R.dimen.tab_height));
|
||||||
|
setClipToPadding(false);
|
||||||
|
setHorizontalScrollBarEnabled(false);
|
||||||
|
mCrumbs = new ArrayList<>();
|
||||||
|
mHistory = new ArrayList<>();
|
||||||
|
mChildFrame = new LinearLayout(getContext());
|
||||||
|
addView(mChildFrame, new ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addHistory(Crumb crumb) {
|
||||||
|
mHistory.add(crumb);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crumb lastHistory() {
|
||||||
|
if (mHistory.size() == 0) return null;
|
||||||
|
return mHistory.get(mHistory.size() - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean popHistory() {
|
||||||
|
if (mHistory.size() == 0) return false;
|
||||||
|
mHistory.remove(mHistory.size() - 1);
|
||||||
|
return mHistory.size() != 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int historySize() {
|
||||||
|
return mHistory.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearHistory() {
|
||||||
|
mHistory.clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void reverseHistory() {
|
||||||
|
Collections.reverse(mHistory);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addCrumb(@NonNull Crumb crumb, boolean refreshLayout) {
|
||||||
|
LinearLayout view = (LinearLayout) LayoutInflater.from(getContext()).inflate(R.layout.bread_crumb, this, false);
|
||||||
|
view.setTag(mCrumbs.size());
|
||||||
|
view.setOnClickListener(this);
|
||||||
|
|
||||||
|
ImageView iv = (ImageView) view.getChildAt(1);
|
||||||
|
if (Build.VERSION.SDK_INT >= 19 && iv.getDrawable() != null) {
|
||||||
|
iv.getDrawable().setAutoMirrored(true);
|
||||||
|
}
|
||||||
|
iv.setVisibility(View.GONE);
|
||||||
|
|
||||||
|
mChildFrame.addView(view, new ViewGroup.LayoutParams(
|
||||||
|
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
|
||||||
|
mCrumbs.add(crumb);
|
||||||
|
if (refreshLayout) {
|
||||||
|
mActive = mCrumbs.size() - 1;
|
||||||
|
requestLayout();
|
||||||
|
}
|
||||||
|
invalidateActivatedAll();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onLayout(boolean changed, int l, int t, int r, int b) {
|
||||||
|
super.onLayout(changed, l, t, r, b);
|
||||||
|
//RTL works fine like this
|
||||||
|
View child = mChildFrame.getChildAt(mActive);
|
||||||
|
if (child != null)
|
||||||
|
smoothScrollTo(child.getLeft(), 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crumb findCrumb(@NonNull File forDir) {
|
||||||
|
for (int i = 0; i < mCrumbs.size(); i++) {
|
||||||
|
if (mCrumbs.get(i).getFile().equals(forDir))
|
||||||
|
return mCrumbs.get(i);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void clearCrumbs() {
|
||||||
|
try {
|
||||||
|
mOldCrumbs = new ArrayList<>(mCrumbs);
|
||||||
|
mCrumbs.clear();
|
||||||
|
mChildFrame.removeAllViews();
|
||||||
|
} catch (IllegalStateException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Crumb getCrumb(int index) {
|
||||||
|
return mCrumbs.get(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setCallback(SelectionCallback callback) {
|
||||||
|
mCallback = callback;
|
||||||
|
}
|
||||||
|
|
||||||
|
private boolean setActive(Crumb newActive) {
|
||||||
|
mActive = mCrumbs.indexOf(newActive);
|
||||||
|
invalidateActivatedAll();
|
||||||
|
boolean success = mActive > -1;
|
||||||
|
if (success)
|
||||||
|
requestLayout();
|
||||||
|
return success;
|
||||||
|
}
|
||||||
|
|
||||||
|
void invalidateActivatedAll() {
|
||||||
|
for (int i = 0; i < mCrumbs.size(); i++) {
|
||||||
|
Crumb crumb = mCrumbs.get(i);
|
||||||
|
invalidateActivated(mChildFrame.getChildAt(i), mActive == mCrumbs.indexOf(crumb), false, i < mCrumbs.size() - 1).setText(crumb.getTitle());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void removeCrumbAt(int index) {
|
||||||
|
mCrumbs.remove(index);
|
||||||
|
mChildFrame.removeViewAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean trim(String path, boolean dir) {
|
||||||
|
if (!dir) return false;
|
||||||
|
int index = -1;
|
||||||
|
for (int i = mCrumbs.size() - 1; i >= 0; i--) {
|
||||||
|
File fi = mCrumbs.get(i).getFile();
|
||||||
|
if (fi.getPath().equals(path)) {
|
||||||
|
index = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean removedActive = index >= mActive;
|
||||||
|
if (index > -1) {
|
||||||
|
while (index <= mCrumbs.size() - 1)
|
||||||
|
removeCrumbAt(index);
|
||||||
|
if (mChildFrame.getChildCount() > 0) {
|
||||||
|
int lastIndex = mCrumbs.size() - 1;
|
||||||
|
invalidateActivated(mChildFrame.getChildAt(lastIndex), mActive == lastIndex, false, false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return removedActive || mCrumbs.size() == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean trim(File file) {
|
||||||
|
return trim(file.getPath(), file.isDirectory());
|
||||||
|
}
|
||||||
|
|
||||||
|
void updateIndices() {
|
||||||
|
for (int i = 0; i < mChildFrame.getChildCount(); i++)
|
||||||
|
mChildFrame.getChildAt(i).setTag(i);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActiveOrAdd(@NonNull Crumb crumb, boolean forceRecreate) {
|
||||||
|
if (forceRecreate || !setActive(crumb)) {
|
||||||
|
clearCrumbs();
|
||||||
|
final List<File> newPathSet = new ArrayList<>();
|
||||||
|
|
||||||
|
newPathSet.add(0, crumb.getFile());
|
||||||
|
|
||||||
|
File p = crumb.getFile();
|
||||||
|
while ((p = p.getParentFile()) != null) {
|
||||||
|
newPathSet.add(0, p);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int index = 0; index < newPathSet.size(); index++) {
|
||||||
|
final File fi = newPathSet.get(index);
|
||||||
|
crumb = new Crumb(fi);
|
||||||
|
|
||||||
|
// Restore scroll positions saved before clearing
|
||||||
|
if (mOldCrumbs != null) {
|
||||||
|
for (Iterator<Crumb> iterator = mOldCrumbs.iterator(); iterator.hasNext(); ) {
|
||||||
|
Crumb old = iterator.next();
|
||||||
|
if (old.equals(crumb)) {
|
||||||
|
crumb.setScrollPosition(old.getScrollPosition());
|
||||||
|
iterator.remove(); // minimize number of linear passes by removing un-used crumbs from history
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
addCrumb(crumb, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
// History no longer needed
|
||||||
|
mOldCrumbs = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public int size() {
|
||||||
|
return mCrumbs.size();
|
||||||
|
}
|
||||||
|
|
||||||
|
private TextView invalidateActivated(View view, final boolean isActive, final boolean noArrowIfAlone, final boolean allowArrowVisible) {
|
||||||
|
int contentColor = isActive ? contentColorActivated : contentColorDeactivated;
|
||||||
|
LinearLayout child = (LinearLayout) view;
|
||||||
|
TextView tv = (TextView) child.getChildAt(0);
|
||||||
|
tv.setTextColor(contentColor);
|
||||||
|
ImageView iv = (ImageView) child.getChildAt(1);
|
||||||
|
iv.setColorFilter(contentColor, PorterDuff.Mode.SRC_IN);
|
||||||
|
if (noArrowIfAlone && getChildCount() == 1)
|
||||||
|
iv.setVisibility(View.GONE);
|
||||||
|
else if (allowArrowVisible)
|
||||||
|
iv.setVisibility(View.VISIBLE);
|
||||||
|
else
|
||||||
|
iv.setVisibility(View.GONE);
|
||||||
|
return tv;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getActiveIndex() {
|
||||||
|
return mActive;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setActivatedContentColor(@ColorInt int contentColorActivated) {
|
||||||
|
this.contentColorActivated = contentColorActivated;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setDeactivatedContentColor(@ColorInt int contentColorDeactivated) {
|
||||||
|
this.contentColorDeactivated = contentColorDeactivated;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onClick(View v) {
|
||||||
|
if (mCallback != null) {
|
||||||
|
int index = (Integer) v.getTag();
|
||||||
|
mCallback.onCrumbSelection(mCrumbs.get(index), index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class SavedStateWrapper implements Parcelable {
|
||||||
|
|
||||||
|
public final int mActive;
|
||||||
|
public final List<Crumb> mCrumbs;
|
||||||
|
public final int mVisibility;
|
||||||
|
|
||||||
|
public SavedStateWrapper(BreadCrumbLayout view) {
|
||||||
|
mActive = view.mActive;
|
||||||
|
mCrumbs = view.mCrumbs;
|
||||||
|
mVisibility = view.getVisibility();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int describeContents() {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void writeToParcel(Parcel dest, int flags) {
|
||||||
|
dest.writeInt(this.mActive);
|
||||||
|
dest.writeTypedList(mCrumbs);
|
||||||
|
dest.writeInt(this.mVisibility);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected SavedStateWrapper(Parcel in) {
|
||||||
|
this.mActive = in.readInt();
|
||||||
|
this.mCrumbs = in.createTypedArrayList(Crumb.CREATOR);
|
||||||
|
this.mVisibility = in.readInt();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static final Creator<SavedStateWrapper> CREATOR = new Creator<SavedStateWrapper>() {
|
||||||
|
public SavedStateWrapper createFromParcel(Parcel source) {
|
||||||
|
return new SavedStateWrapper(source);
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedStateWrapper[] newArray(int size) {
|
||||||
|
return new SavedStateWrapper[size];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public SavedStateWrapper getStateWrapper() {
|
||||||
|
return new SavedStateWrapper(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void restoreFromStateWrapper(SavedStateWrapper mSavedState) {
|
||||||
|
if (mSavedState != null) {
|
||||||
|
mActive = mSavedState.mActive;
|
||||||
|
for (Crumb c : mSavedState.mCrumbs) {
|
||||||
|
addCrumb(c, false);
|
||||||
|
}
|
||||||
|
requestLayout();
|
||||||
|
setVisibility(mSavedState.mVisibility);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
10
app/src/main/res/drawable/ic_folder_special_white_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_folder_special_white_24dp.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M20,6h-8l-2,-2L4,4c-1.1,0 -2,0.9 -2,2v12c0,1.1 0.9,2 2,2h16c1.1,0 2,-0.9 2,-2L22,8c0,-1.1 -0.9,-2 -2,-2zM17.94,17L15,15.28 12.06,17l0.78,-3.33 -2.59,-2.24 3.41,-0.29L15,8l1.34,3.14 3.41,0.29 -2.59,2.24 0.78,3.33z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M6,2c-1.1,0 -1.99,0.9 -1.99,2L4,20c0,1.1 0.89,2 1.99,2L18,22c1.1,0 2,-0.9 2,-2L20,8l-6,-6L6,2zM13,9L13,3.5L18.5,9L13,9z" />
|
||||||
|
</vector>
|
||||||
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z" />
|
||||||
|
</vector>
|
||||||
10
app/src/main/res/drawable/ic_music_note_white_24dp.xml
Normal file
10
app/src/main/res/drawable/ic_music_note_white_24dp.xml
Normal file
|
|
@ -0,0 +1,10 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
||||||
|
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:height="24dp"
|
||||||
|
android:viewportHeight="24.0"
|
||||||
|
android:viewportWidth="24.0"
|
||||||
|
android:width="24dp">
|
||||||
|
<path
|
||||||
|
android:fillColor="#FFFFFF"
|
||||||
|
android:pathData="M12,3v10.55c-0.59,-0.34 -1.27,-0.55 -2,-0.55 -2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4V7h4V3h-6z" />
|
||||||
|
</vector>
|
||||||
38
app/src/main/res/layout/bread_crumb.xml
Normal file
38
app/src/main/res/layout/bread_crumb.xml
Normal file
|
|
@ -0,0 +1,38 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:background="?rectSelector"
|
||||||
|
android:gravity="center_vertical|start"
|
||||||
|
android:minHeight="@dimen/tab_height"
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:paddingEnd="4dp"
|
||||||
|
android:paddingLeft="12dp"
|
||||||
|
android:paddingRight="4dp"
|
||||||
|
android:paddingStart="12dp">
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:fontFamily="sans-serif-medium"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:textAllCaps="true"
|
||||||
|
android:textColor="#fff"
|
||||||
|
android:textSize="14sp"
|
||||||
|
tools:ignore="RtlSymmetry,UnusedAttribute"
|
||||||
|
tools:text="Storage"
|
||||||
|
tools:textColor="#000" />
|
||||||
|
|
||||||
|
<android.support.v7.widget.AppCompatImageView
|
||||||
|
android:layout_width="24dp"
|
||||||
|
android:layout_height="24dp"
|
||||||
|
android:scaleType="fitXY"
|
||||||
|
android:visibility="gone"
|
||||||
|
app:srcCompat="@drawable/ic_keyboard_arrow_right_white_24dp"
|
||||||
|
tools:tint="#000"
|
||||||
|
tools:visibility="visible" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
@ -1,50 +1,87 @@
|
||||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
xmlns:tools="http://schemas.android.com/tools"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="match_parent"
|
||||||
tools:ignore="UnusedAttribute">
|
android:orientation="vertical"
|
||||||
|
tools:context="com.kabouzeid.gramophone.ui.fragments.FolderFragment">
|
||||||
|
|
||||||
<LinearLayout
|
<FrameLayout
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="match_parent"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:elevation="0dp"
|
||||||
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<include layout="@layout/status_bar" />
|
<include layout="@layout/status_bar" />
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
<FrameLayout
|
<android.support.design.widget.CoordinatorLayout
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent">
|
||||||
|
|
||||||
|
<android.support.design.widget.AppBarLayout
|
||||||
|
android:id="@+id/appbar"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:elevation="@dimen/toolbar_elevation"
|
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
<android.support.v7.widget.Toolbar
|
<FrameLayout
|
||||||
android:id="@+id/toolbar"
|
|
||||||
style="@style/Toolbar"
|
|
||||||
android:background="@android:color/transparent" />
|
|
||||||
|
|
||||||
<ViewStub
|
|
||||||
android:id="@+id/cab_stub"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="?attr/actionBarSize" />
|
android:layout_height="wrap_content"
|
||||||
|
app:layout_scrollFlags="scroll|enterAlways">
|
||||||
|
|
||||||
|
<android.support.v7.widget.Toolbar
|
||||||
|
android:id="@+id/toolbar"
|
||||||
|
style="@style/Toolbar"
|
||||||
|
android:elevation="0dp"
|
||||||
|
tools:ignore="UnusedAttribute">
|
||||||
|
|
||||||
|
</android.support.v7.widget.Toolbar>
|
||||||
|
|
||||||
|
<ViewStub
|
||||||
|
android:id="@+id/cab_stub"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="?attr/actionBarSize" />
|
||||||
|
|
||||||
|
</FrameLayout>
|
||||||
|
|
||||||
|
<com.kabouzeid.gramophone.views.BreadCrumbLayout
|
||||||
|
android:id="@+id/bread_crumbs"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="@dimen/tab_height"
|
||||||
|
android:paddingEnd="8dp"
|
||||||
|
android:paddingLeft="60dp"
|
||||||
|
android:paddingRight="8dp"
|
||||||
|
android:paddingStart="60dp" />
|
||||||
|
|
||||||
|
</android.support.design.widget.AppBarLayout>
|
||||||
|
|
||||||
|
<FrameLayout android:id="@+id/container"
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
app:layout_behavior="@string/appbar_scrolling_view_behavior">
|
||||||
|
|
||||||
|
<com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView
|
||||||
|
android:id="@+id/recycler_view"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:clipToPadding="false"
|
||||||
|
android:scrollbars="none" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:id="@android:id/empty"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_gravity="center"
|
||||||
|
android:fontFamily="sans-serif-light"
|
||||||
|
android:text="@string/empty"
|
||||||
|
android:textColor="?android:textColorSecondary"
|
||||||
|
android:textSize="@dimen/empty_text_size" />
|
||||||
|
|
||||||
</FrameLayout>
|
</FrameLayout>
|
||||||
|
|
||||||
<android.support.v7.widget.RecyclerView
|
</android.support.design.widget.CoordinatorLayout>
|
||||||
android:id="@+id/recycler_view"
|
|
||||||
android:layout_width="match_parent"
|
|
||||||
android:layout_height="match_parent"
|
|
||||||
android:scrollbars="vertical" />
|
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<TextView
|
|
||||||
android:id="@android:id/empty"
|
|
||||||
android:layout_width="wrap_content"
|
|
||||||
android:layout_height="wrap_content"
|
|
||||||
android:layout_gravity="center"
|
|
||||||
android:fontFamily="sans-serif-light"
|
|
||||||
android:text="Placeholder"
|
|
||||||
android:textColor="?android:textColorSecondary"
|
|
||||||
android:textSize="@dimen/empty_text_size" />
|
|
||||||
|
|
||||||
</FrameLayout>
|
|
||||||
|
|
|
||||||
11
app/src/main/res/menu/menu_folders.xml
Normal file
11
app/src/main/res/menu/menu_folders.xml
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<item
|
||||||
|
android:id="@+id/action_go_to_standard_folder"
|
||||||
|
android:icon="@drawable/ic_folder_special_white_24dp"
|
||||||
|
android:title="@string/action_go_to_standard_folder"
|
||||||
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
|
</menu>
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
android:title="@string/action_set_as_ringtone"
|
android:title="@string/action_set_as_ringtone"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_disk"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
android:title="@string/action_set_as_ringtone"
|
android:title="@string/action_set_as_ringtone"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_disk"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -42,7 +42,7 @@
|
||||||
android:title="@string/action_set_as_ringtone"
|
android:title="@string/action_set_as_ringtone"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_disk"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -38,7 +38,7 @@
|
||||||
android:title="@string/action_set_as_ringtone"
|
android:title="@string/action_set_as_ringtone"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_disk"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="never" />
|
app:showAsAction="never" />
|
||||||
</menu>
|
</menu>
|
||||||
|
|
@ -15,7 +15,7 @@
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
||||||
<item
|
<item
|
||||||
android:id="@+id/action_delete_from_disk"
|
android:id="@+id/action_delete_from_device"
|
||||||
android:icon="@drawable/ic_delete_white_24dp"
|
android:icon="@drawable/ic_delete_white_24dp"
|
||||||
android:title="@string/action_delete_from_device"
|
android:title="@string/action_delete_from_device"
|
||||||
app:showAsAction="ifRoom" />
|
app:showAsAction="ifRoom" />
|
||||||
|
|
|
||||||
|
|
@ -96,6 +96,7 @@
|
||||||
<string name="action_shuffle_artist">Shuffle artist</string>
|
<string name="action_shuffle_artist">Shuffle artist</string>
|
||||||
<string name="action_shuffle_playlist">Shuffle playlist</string>
|
<string name="action_shuffle_playlist">Shuffle playlist</string>
|
||||||
<string name="action_clear_playing_queue">Clear playing queue</string>
|
<string name="action_clear_playing_queue">Clear playing queue</string>
|
||||||
|
<string name="action_go_to_standard_folder">Go to standard folder</string>
|
||||||
<string name="last_opened">Last opened</string>
|
<string name="last_opened">Last opened</string>
|
||||||
<string name="light_theme_name">Light</string>
|
<string name="light_theme_name">Light</string>
|
||||||
<string name="dark_theme_name">Dark</string>
|
<string name="dark_theme_name">Dark</string>
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue