Made progress with the folder feature.

This commit is contained in:
Karim Abou Zeid 2016-03-16 00:41:17 +01:00
commit 1e6ac6a227
38 changed files with 1423 additions and 148 deletions

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

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

View 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="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>

View 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="M8.59,16.34l4.58,-4.59 -4.58,-4.59L10,5.75l6,6 -6,6z" />
</vector>

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

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

View file

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

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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