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 com.kabouzeid.gramophone.R;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AlbumsPagerFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.ArtistsPagerFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.PlaylistsPagerFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.SongsPagerFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.AlbumsFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.ArtistsFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.PlaylistsFragment;
|
||||
import com.kabouzeid.gramophone.ui.fragments.libraryfragments.SongsFragment;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.ArrayList;
|
||||
|
|
@ -107,10 +107,10 @@ public class MusicLibraryPagerAdapter extends FragmentPagerAdapter {
|
|||
}
|
||||
|
||||
public enum MusicFragments {
|
||||
SONG(SongsPagerFragment.class),
|
||||
ALBUM(AlbumsPagerFragment.class),
|
||||
ARTIST(ArtistsPagerFragment.class),
|
||||
PLAYLIST(PlaylistsPagerFragment.class);
|
||||
SONG(SongsFragment.class),
|
||||
ALBUM(AlbumsFragment.class),
|
||||
ARTIST(ArtistsFragment.class),
|
||||
PLAYLIST(PlaylistsFragment.class);
|
||||
|
||||
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
|
||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Album> selection) {
|
||||
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");
|
||||
break;
|
||||
case R.id.action_add_to_playlist:
|
||||
|
|
|
|||
|
|
@ -173,7 +173,7 @@ public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolde
|
|||
@Override
|
||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Artist> selection) {
|
||||
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");
|
||||
break;
|
||||
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) {
|
||||
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");
|
||||
break;
|
||||
case R.id.action_add_to_playlist:
|
||||
|
|
|
|||
|
|
@ -172,7 +172,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
|
|||
@Override
|
||||
protected void onMultipleItemAction(@NonNull MenuItem menuItem, @NonNull ArrayList<Song> selection) {
|
||||
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");
|
||||
break;
|
||||
case R.id.action_add_to_playlist:
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ public class SongMenuHelper {
|
|||
case R.id.action_share:
|
||||
activity.startActivity(Intent.createChooser(MusicUtil.createShareSongFileIntent(song), null));
|
||||
return true;
|
||||
case R.id.action_delete_from_disk:
|
||||
case R.id.action_delete_from_device:
|
||||
DeleteSongsDialog.create(song).show(activity.getSupportFragmentManager(), "DELETE_SONGS");
|
||||
return true;
|
||||
case R.id.action_add_to_playlist:
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
package com.kabouzeid.gramophone.interfaces;
|
||||
|
||||
import android.support.annotation.NonNull;
|
||||
|
||||
import com.afollestad.materialcab.MaterialCab;
|
||||
|
||||
/**
|
||||
|
|
@ -7,5 +9,6 @@ import com.afollestad.materialcab.MaterialCab;
|
|||
*/
|
||||
public interface CabHolder {
|
||||
|
||||
@NonNull
|
||||
MaterialCab openCab(final int menuRes, final MaterialCab.Callback callback);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -83,12 +83,12 @@ public class SongLoader {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
public static Cursor makeSongCursor(@NonNull final Context context, final String selection, final String[] values) {
|
||||
return makeSongCursor(context, selection, values, PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||
public static Cursor makeSongCursor(@NonNull final Context context, @Nullable final String selection, final String[] selectionValues) {
|
||||
return makeSongCursor(context, selection, selectionValues, PreferenceUtil.getInstance(context).getSongSortOrder());
|
||||
}
|
||||
|
||||
@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;
|
||||
if (selection != null && !selection.trim().equals("")) {
|
||||
baseSelection += " AND " + selection;
|
||||
|
|
@ -109,7 +109,7 @@ public class SongLoader {
|
|||
AudioColumns.ARTIST_ID,// 9
|
||||
AudioColumns.ARTIST,// 10
|
||||
|
||||
}, baseSelection, values, sortOrder);
|
||||
}, baseSelection, selectionValues, sortOrder);
|
||||
} catch (SecurityException e) {
|
||||
return null;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,7 +23,8 @@ import android.support.annotation.Nullable;
|
|||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
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
|
||||
|
|
@ -36,97 +37,77 @@ public class SortedCursor extends AbstractCursor {
|
|||
// 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;
|
||||
private ArrayList<String> mMissingValues;
|
||||
// this contains the mapped cursor positions and afterwards the extra ids that weren't found
|
||||
private HashMap<Long, Integer> mMapCursorPositions;
|
||||
// extra we want to store with the cursor
|
||||
private ArrayList<Object> mExtraData;
|
||||
private HashMap<String, 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 SortedCursor(final Cursor cursor, final long[] order, final String columnName,
|
||||
final List<?> extraData) {
|
||||
if (cursor == null) {
|
||||
throw new IllegalArgumentException("Non-null cursor is needed");
|
||||
}
|
||||
|
||||
@DebugLog
|
||||
public SortedCursor(@NonNull final Cursor cursor, @Nullable final String[] order, final String columnName) {
|
||||
mCursor = cursor;
|
||||
mMissingIds = buildCursorPositionMapping(order, columnName, extraData);
|
||||
mMissingValues = 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
|
||||
* @param extraData Extra data we want to add to the cursor
|
||||
* @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, @Nullable final List<?> extraData) {
|
||||
ArrayList<Long> missingIds = new ArrayList<>();
|
||||
private ArrayList<String> buildCursorPositionMapping(@Nullable final String[] order, final String columnName) {
|
||||
ArrayList<String> missingValues = new ArrayList<>();
|
||||
|
||||
mOrderedPositions = new ArrayList<>(mCursor.getCount());
|
||||
mExtraData = new ArrayList<>();
|
||||
|
||||
mMapCursorPositions = new HashMap<>(mCursor.getCount());
|
||||
final int idPosition = mCursor.getColumnIndex(columnName);
|
||||
final int valueColumnIndex = 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());
|
||||
mMapCursorPositions.put(mCursor.getString(valueColumnIndex), 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);
|
||||
if (extraData != null) {
|
||||
mExtraData.add(extraData.get(i));
|
||||
if (order != null) {
|
||||
// now create the ordered positions to map to the internal cursor given the
|
||||
// external sort order
|
||||
for (final String value : order) {
|
||||
if (mMapCursorPositions.containsKey(value)) {
|
||||
mOrderedPositions.add(mMapCursorPositions.get(value));
|
||||
mMapCursorPositions.remove(value);
|
||||
} else {
|
||||
missingValues.add(value);
|
||||
}
|
||||
} else {
|
||||
missingIds.add(id);
|
||||
}
|
||||
}
|
||||
|
||||
mCursor.moveToFirst();
|
||||
}
|
||||
|
||||
return missingIds;
|
||||
return missingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that weren't found in the underlying cursor
|
||||
*/
|
||||
public ArrayList<Long> getMissingIds() {
|
||||
return mMissingIds;
|
||||
public ArrayList<String> getMissingValues() {
|
||||
return mMissingValues;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the list of ids that were in the underlying cursor but not part of the ordered list
|
||||
*/
|
||||
@NonNull
|
||||
public Collection<Long> getExtraIds() {
|
||||
public Collection<String> getExtraValues() {
|
||||
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
|
||||
public void 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
|
||||
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
|
||||
if (retCursor != null) {
|
||||
|
|
@ -59,7 +59,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
|
||||
@Nullable
|
||||
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
|
||||
if (retCursor != null) {
|
||||
|
|
@ -74,7 +74,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
}
|
||||
|
||||
@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
|
||||
Cursor songs = HistoryStore.getInstance(context).queryRecentIds();
|
||||
|
||||
|
|
@ -89,7 +89,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
}
|
||||
|
||||
@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
|
||||
Cursor songs = SongPlayCountStore.getInstance(context).getTopPlayedResults(NUMBER_OF_TOP_TRACKS);
|
||||
|
||||
|
|
@ -104,8 +104,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
}
|
||||
|
||||
@Nullable
|
||||
private static SortedCursor makeSortedCursor(@NonNull final Context context, @Nullable final Cursor cursor,
|
||||
final int idColumn) {
|
||||
private static SortedLongCursor makeSortedCursor(@NonNull final Context context, @Nullable final Cursor cursor, final int idColumn) {
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
// create the list of ids to select against
|
||||
StringBuilder selection = new StringBuilder();
|
||||
|
|
@ -133,7 +132,7 @@ public class TopAndRecentlyPlayedTracksLoader {
|
|||
Cursor songCursor = SongLoader.makeSongCursor(context, selection.toString(), null);
|
||||
if (songCursor != null) {
|
||||
// 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)
|
||||
DrawerLayout drawerLayout;
|
||||
|
||||
@Nullable
|
||||
MainActivityFragmentCallbacks currentFragment;
|
||||
|
||||
@Nullable
|
||||
|
|
@ -88,7 +89,11 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
|||
|
||||
setUpDrawerLayout();
|
||||
|
||||
setMusicChooser(PreferenceUtil.getInstance(this).getLastMusicChooser());
|
||||
if (savedInstanceState == null) {
|
||||
setMusicChooser(PreferenceUtil.getInstance(this).getLastMusicChooser());
|
||||
} else {
|
||||
restoreCurrentFragment();
|
||||
}
|
||||
|
||||
if (!checkShowIntro()) {
|
||||
checkShowChangelog();
|
||||
|
|
@ -100,20 +105,24 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
|||
switch (key) {
|
||||
case LIBRARY:
|
||||
navigationView.setCheckedItem(R.id.nav_library);
|
||||
setCurrentFragment(new LibraryFragment());
|
||||
setCurrentFragment(LibraryFragment.newInstance());
|
||||
break;
|
||||
case FOLDERS:
|
||||
navigationView.setCheckedItem(R.id.nav_folders);
|
||||
setCurrentFragment(new FolderFragment());
|
||||
setCurrentFragment(FolderFragment.newInstance());
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void setCurrentFragment(Fragment fragment) {
|
||||
private void setCurrentFragment(@SuppressWarnings("NullableProblems") Fragment fragment) {
|
||||
getSupportFragmentManager().beginTransaction().replace(R.id.fragment_container, fragment, LibraryFragment.TAG).commit();
|
||||
currentFragment = (MainActivityFragmentCallbacks) fragment;
|
||||
}
|
||||
|
||||
private void restoreCurrentFragment() {
|
||||
currentFragment = (MainActivityFragmentCallbacks) getSupportFragmentManager().findFragmentById(R.id.fragment_container);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
|
||||
super.onActivityResult(requestCode, resultCode, data);
|
||||
|
|
@ -254,12 +263,13 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
|||
return super.onOptionsItemSelected(item);
|
||||
}
|
||||
|
||||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (drawerLayout.isDrawerOpen(navigationView)) drawerLayout.closeDrawers();
|
||||
else if (currentFragment == null || !currentFragment.onBackPressed())
|
||||
super.onBackPressed();
|
||||
public boolean handleBackPress() {
|
||||
if (drawerLayout.isDrawerOpen(navigationView)) {
|
||||
drawerLayout.closeDrawers();
|
||||
return true;
|
||||
}
|
||||
return super.handleBackPress() || (currentFragment != null && currentFragment.handleBackPress());
|
||||
}
|
||||
|
||||
private void handlePlaybackIntent(@Nullable Intent intent) {
|
||||
|
|
@ -391,6 +401,6 @@ public class MainActivity extends AbsSlidingMusicPanelActivity
|
|||
}
|
||||
|
||||
public interface MainActivityFragmentCallbacks {
|
||||
boolean onBackPressed();
|
||||
boolean handleBackPress();
|
||||
}
|
||||
}
|
||||
|
|
@ -173,13 +173,18 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi
|
|||
|
||||
@Override
|
||||
public void onBackPressed() {
|
||||
if (!handleBackPress())
|
||||
super.onBackPressed();
|
||||
}
|
||||
|
||||
public boolean handleBackPress() {
|
||||
if (slidingUpPanelLayout.getPanelHeight() != 0 && playerFragment.onBackPressed())
|
||||
return;
|
||||
return true;
|
||||
if (getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) {
|
||||
collapsePanel();
|
||||
return;
|
||||
return true;
|
||||
}
|
||||
super.onBackPressed();
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
|||
|
|
@ -1,24 +1,141 @@
|
|||
package com.kabouzeid.gramophone.ui.fragments;
|
||||
|
||||
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
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.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.Menu;
|
||||
import android.view.MenuInflater;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
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.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.util.PhonographColorUtil;
|
||||
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 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();
|
||||
|
||||
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 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
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
|
|
@ -30,16 +147,249 @@ public class FolderFragment extends AbsMainActivityFragment implements MainActiv
|
|||
@Override
|
||||
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
|
||||
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
|
||||
public void onDestroyView() {
|
||||
appbar.removeOnOffsetChangedListener(this);
|
||||
ButterKnife.unbind(this);
|
||||
super.onDestroyView();
|
||||
}
|
||||
|
||||
@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;
|
||||
}
|
||||
|
||||
@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.ui.activities.MainActivity;
|
||||
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.PhonographColorUtil;
|
||||
import com.kabouzeid.gramophone.util.PreferenceUtil;
|
||||
|
|
@ -55,6 +55,10 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
private MusicLibraryPagerAdapter pagerAdapter;
|
||||
private MaterialCab cab;
|
||||
|
||||
public static LibraryFragment newInstance() {
|
||||
return new LibraryFragment();
|
||||
}
|
||||
|
||||
public LibraryFragment() {
|
||||
}
|
||||
|
||||
|
|
@ -87,7 +91,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
appbar.setBackgroundColor(primaryColor);
|
||||
toolbar.setBackgroundColor(primaryColor);
|
||||
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);
|
||||
}
|
||||
|
||||
|
|
@ -120,11 +124,12 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
return pager.getCurrentItem() == MusicLibraryPagerAdapter.MusicFragments.PLAYLIST.ordinal();
|
||||
}
|
||||
|
||||
@NonNull
|
||||
@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();
|
||||
cab = new MaterialCab(getMainActivity(), R.id.cab_stub)
|
||||
.setMenu(menu)
|
||||
.setMenu(menuRes)
|
||||
.setCloseDrawableRes(R.drawable.ic_close_white_24dp)
|
||||
.setBackgroundColor(PhonographColorUtil.shiftBackgroundColorForLightText(ThemeStore.primaryColor(getActivity())))
|
||||
.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);
|
||||
}
|
||||
Fragment currentFragment = getCurrentFragment();
|
||||
if (currentFragment instanceof AbsLibraryRecyclerViewCustomGridSizePagerFragment && currentFragment.isAdded()) {
|
||||
AbsLibraryRecyclerViewCustomGridSizePagerFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryRecyclerViewCustomGridSizePagerFragment) currentFragment;
|
||||
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment && currentFragment.isAdded()) {
|
||||
AbsLibraryPagerRecyclerViewCustomGridSizeFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
|
||||
|
||||
MenuItem gridSizeItem = menu.findItem(R.id.action_grid_size);
|
||||
if (Util.isLandscape(getResources())) {
|
||||
|
|
@ -171,8 +176,8 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
@Override
|
||||
public boolean onOptionsItemSelected(@NonNull MenuItem item) {
|
||||
Fragment currentFragment = getCurrentFragment();
|
||||
if (currentFragment instanceof AbsLibraryRecyclerViewCustomGridSizePagerFragment) {
|
||||
AbsLibraryRecyclerViewCustomGridSizePagerFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryRecyclerViewCustomGridSizePagerFragment) currentFragment;
|
||||
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment) {
|
||||
AbsLibraryPagerRecyclerViewCustomGridSizeFragment absLibraryRecyclerViewCustomGridSizeFragment = (AbsLibraryPagerRecyclerViewCustomGridSizeFragment) currentFragment;
|
||||
if (item.getItemId() == R.id.action_colored_footers) {
|
||||
item.setChecked(!item.isChecked());
|
||||
absLibraryRecyclerViewCustomGridSizeFragment.setAndSaveUsePalette(item.isChecked());
|
||||
|
|
@ -204,7 +209,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
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()) {
|
||||
case 1:
|
||||
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;
|
||||
switch (item.getItemId()) {
|
||||
case R.id.action_grid_size_1:
|
||||
|
|
@ -290,7 +295,7 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean onBackPressed() {
|
||||
public boolean handleBackPress() {
|
||||
if (cab != null && cab.isActive()) {
|
||||
cab.finish();
|
||||
return true;
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@ import com.kabouzeid.gramophone.util.Util;
|
|||
/**
|
||||
* @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 boolean usePaletteInitialized;
|
||||
|
|
@ -14,11 +14,9 @@ import android.view.ViewGroup;
|
|||
import android.widget.TextView;
|
||||
|
||||
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.interfaces.MusicServiceEventListener;
|
||||
import com.kabouzeid.gramophone.util.ViewUtil;
|
||||
import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView;
|
||||
|
||||
import butterknife.Bind;
|
||||
|
|
@ -27,9 +25,9 @@ import butterknife.ButterKnife;
|
|||
/**
|
||||
* @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)
|
||||
View container;
|
||||
|
|
@ -63,11 +61,7 @@ public abstract class AbsLibraryRecyclerViewPagerFragment<A extends RecyclerView
|
|||
|
||||
private void setUpRecyclerView() {
|
||||
if (recyclerView instanceof FastScrollRecyclerView) {
|
||||
int accentColor = 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));
|
||||
ViewUtil.setUpFastScrollRecyclerViewColor(getActivity(), ((FastScrollRecyclerView) recyclerView), ThemeStore.accentColor(getActivity()));
|
||||
}
|
||||
invalidateLayoutManager();
|
||||
invalidateAdapter();
|
||||
|
|
@ -11,8 +11,8 @@ import com.kabouzeid.gramophone.util.PreferenceUtil;
|
|||
/**
|
||||
* @author Karim Abou Zeid (kabouzeid)
|
||||
*/
|
||||
public class AlbumsPagerFragment extends AbsLibraryRecyclerViewCustomGridSizePagerFragment<AlbumAdapter, GridLayoutManager> {
|
||||
public static final String TAG = AlbumsPagerFragment.class.getSimpleName();
|
||||
public class AlbumsFragment extends AbsLibraryPagerRecyclerViewCustomGridSizeFragment<AlbumAdapter, GridLayoutManager> {
|
||||
public static final String TAG = AlbumsFragment.class.getSimpleName();
|
||||
|
||||
@Override
|
||||
protected GridLayoutManager createLayoutManager() {
|
||||
|
|
@ -11,9 +11,9 @@ import com.kabouzeid.gramophone.util.PreferenceUtil;
|
|||
/**
|
||||
* @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
|
||||
@Override
|
||||
|
|
@ -16,9 +16,9 @@ import java.util.ArrayList;
|
|||
/**
|
||||
* @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
|
||||
@Override
|
||||
|
|
@ -15,9 +15,9 @@ import java.util.ArrayList;
|
|||
/**
|
||||
* @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
|
||||
@Override
|
||||
|
|
@ -13,6 +13,7 @@ import android.net.ConnectivityManager;
|
|||
import android.net.NetworkInfo;
|
||||
import android.os.Build;
|
||||
import android.support.annotation.AttrRes;
|
||||
import android.support.annotation.ColorInt;
|
||||
import android.support.annotation.DrawableRes;
|
||||
import android.support.annotation.NonNull;
|
||||
import android.support.annotation.Nullable;
|
||||
|
|
@ -94,7 +95,7 @@ public class Util {
|
|||
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) {
|
||||
// vector support
|
||||
context = TintContextWrapper.wrap(context);
|
||||
|
|
|
|||
|
|
@ -17,6 +17,12 @@ import android.view.View;
|
|||
import android.view.animation.PathInterpolator;
|
||||
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)
|
||||
*/
|
||||
|
|
@ -71,4 +77,11 @@ public class ViewUtil {
|
|||
|
||||
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"
|
||||
android:layout_width="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_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
android:layout_height="wrap_content"
|
||||
android:elevation="0dp"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<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_height="wrap_content"
|
||||
android:elevation="@dimen/toolbar_elevation"
|
||||
tools:ignore="UnusedAttribute">
|
||||
|
||||
<android.support.v7.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
style="@style/Toolbar"
|
||||
android:background="@android:color/transparent" />
|
||||
|
||||
<ViewStub
|
||||
android:id="@+id/cab_stub"
|
||||
<FrameLayout
|
||||
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>
|
||||
|
||||
<android.support.v7.widget.RecyclerView
|
||||
android:id="@+id/recycler_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:scrollbars="vertical" />
|
||||
</android.support.design.widget.CoordinatorLayout>
|
||||
|
||||
</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>
|
||||
</LinearLayout>
|
||||
|
|
|
|||
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"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_delete_from_disk"
|
||||
android:id="@+id/action_delete_from_device"
|
||||
android:title="@string/action_delete_from_device"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
android:title="@string/action_set_as_ringtone"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_delete_from_disk"
|
||||
android:id="@+id/action_delete_from_device"
|
||||
android:title="@string/action_delete_from_device"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
@ -42,7 +42,7 @@
|
|||
android:title="@string/action_set_as_ringtone"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_delete_from_disk"
|
||||
android:id="@+id/action_delete_from_device"
|
||||
android:title="@string/action_delete_from_device"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
@ -38,7 +38,7 @@
|
|||
android:title="@string/action_set_as_ringtone"
|
||||
app:showAsAction="never" />
|
||||
<item
|
||||
android:id="@+id/action_delete_from_disk"
|
||||
android:id="@+id/action_delete_from_device"
|
||||
android:title="@string/action_delete_from_device"
|
||||
app:showAsAction="never" />
|
||||
</menu>
|
||||
|
|
@ -15,7 +15,7 @@
|
|||
app:showAsAction="ifRoom" />
|
||||
|
||||
<item
|
||||
android:id="@+id/action_delete_from_disk"
|
||||
android:id="@+id/action_delete_from_device"
|
||||
android:icon="@drawable/ic_delete_white_24dp"
|
||||
android:title="@string/action_delete_from_device"
|
||||
app:showAsAction="ifRoom" />
|
||||
|
|
|
|||
|
|
@ -96,6 +96,7 @@
|
|||
<string name="action_shuffle_artist">Shuffle artist</string>
|
||||
<string name="action_shuffle_playlist">Shuffle playlist</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="light_theme_name">Light</string>
|
||||
<string name="dark_theme_name">Dark</string>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue