This commit is contained in:
Karim Abou Zeid 2018-06-05 22:45:29 +02:00
commit 614f28e0c9
23 changed files with 220 additions and 89 deletions

View file

@ -1,5 +1,6 @@
package com.kabouzeid.gramophone.adapter;
import android.content.Context;
import android.graphics.PorterDuff;
import android.os.Build;
import android.support.annotation.LayoutRes;
@ -11,8 +12,10 @@ import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.PopupMenu;
import android.widget.Toast;
import com.kabouzeid.appthemehelper.util.ATHUtil;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.base.AbsMultiSelectAdapter;
import com.kabouzeid.gramophone.adapter.base.MediaEntryViewHolder;
@ -22,6 +25,7 @@ import com.kabouzeid.gramophone.helper.menu.PlaylistMenuHelper;
import com.kabouzeid.gramophone.helper.menu.SongsMenuHelper;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.loader.PlaylistSongLoader;
import com.kabouzeid.gramophone.misc.WeakContextAsyncTask;
import com.kabouzeid.gramophone.model.AbsCustomPlaylist;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.Song;
@ -29,7 +33,9 @@ import com.kabouzeid.gramophone.model.smartplaylist.AbsSmartPlaylist;
import com.kabouzeid.gramophone.model.smartplaylist.LastAddedPlaylist;
import com.kabouzeid.gramophone.util.MusicUtil;
import com.kabouzeid.gramophone.util.NavigationUtil;
import com.kabouzeid.gramophone.util.PlaylistsUtil;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
@ -149,12 +155,56 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewH
DeletePlaylistDialog.create(selection).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST");
}
break;
case R.id.action_save_playlist:
if (selection.size() == 1) {
PlaylistMenuHelper.handleMenuClick(activity, selection.get(0), menuItem);
} else {
new SavePlaylistsAsyncTask(activity).execute(selection);
}
break;
default:
SongsMenuHelper.handleMenuClick(activity, getSongList(selection), menuItem.getItemId());
break;
}
}
private static class SavePlaylistsAsyncTask extends WeakContextAsyncTask<ArrayList<Playlist>, String, String> {
public SavePlaylistsAsyncTask(Context context) {
super(context);
}
@Override
protected String doInBackground(ArrayList<Playlist>... params) {
int successes = 0;
int failures = 0;
String dir = "";
for (Playlist playlist : params[0]) {
try {
dir = PlaylistsUtil.savePlaylist(App.getInstance().getApplicationContext(), playlist).getParent();
successes++;
} catch (IOException e) {
failures++;
e.printStackTrace();
}
}
return failures == 0
? String.format(App.getInstance().getApplicationContext().getString(R.string.saved_x_playlists_to_x), successes, dir)
: String.format(App.getInstance().getApplicationContext().getString(R.string.saved_x_playlists_to_x_failed_to_save_x), successes, dir, failures);
}
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
Context context = getContext();
if (context != null) {
Toast.makeText(context, string, Toast.LENGTH_LONG).show();
}
}
}
@NonNull
private ArrayList<Song> getSongList(@NonNull List<Playlist> playlists) {
final ArrayList<Song> songs = new ArrayList<>();

View file

@ -198,7 +198,7 @@ public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder,
sectionName = dataSet.get(position).getArtistName();
break;
case SortOrder.AlbumSortOrder.ALBUM_YEAR:
return Integer.toString(dataSet.get(position).getYear());
return MusicUtil.getYearString(dataSet.get(position).getYear());
}
return MusicUtil.getSectionName(sectionName);

View file

@ -16,6 +16,7 @@ import com.kabouzeid.gramophone.glide.SongGlideRequest;
import com.kabouzeid.gramophone.helper.HorizontalAdapterHelper;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.model.Album;
import com.kabouzeid.gramophone.util.MusicUtil;
import java.util.ArrayList;
@ -76,11 +77,7 @@ public class HorizontalAlbumAdapter extends AlbumAdapter {
@Override
protected String getAlbumText(Album album) {
int year = album.getYear();
if(year > 0) {
return String.valueOf(year);
}
return "-";
return MusicUtil.getYearString(album.getYear());
}
@Override

View file

@ -31,37 +31,51 @@ public abstract class AbsMultiSelectAdapter<VH extends RecyclerView.ViewHolder,
this.context = context;
}
protected void overrideMultiSelectMenuRes(@MenuRes int menuRes) {
protected void setMultiSelectMenuRes(@MenuRes int menuRes) {
this.menuRes = menuRes;
}
protected boolean toggleChecked(final int position) {
if (cabHolder != null) {
openCabIfNecessary();
I identifier = getIdentifier(position);
if (identifier == null) return false;
if (!checked.remove(identifier)) checked.add(identifier);
notifyItemChanged(position);
final int size = checked.size();
if (size <= 0) cab.finish();
else if (size == 1) cab.setTitle(getName(checked.get(0)));
else if (size > 1) cab.setTitle(context.getString(R.string.x_selected, size));
updateCab();
return true;
}
return false;
}
private void openCabIfNecessary() {
protected void checkAll() {
if (cabHolder != null) {
checked.clear();
for (int i = 0; i < getItemCount(); i++) {
I identifier = getIdentifier(i);
if (identifier != null) {
checked.add(identifier);
}
}
notifyDataSetChanged();
updateCab();
}
}
private void updateCab() {
if (cabHolder != null) {
if (cab == null || !cab.isActive()) {
cab = cabHolder.openCab(menuRes, this);
}
final int size = checked.size();
if (size <= 0) cab.finish();
else if (size == 1) cab.setTitle(getName(checked.get(0)));
else cab.setTitle(context.getString(R.string.x_selected, size));
}
}
private void unCheckAll() {
private void clearChecked() {
checked.clear();
notifyDataSetChanged();
}
@ -81,15 +95,19 @@ public abstract class AbsMultiSelectAdapter<VH extends RecyclerView.ViewHolder,
@Override
public boolean onCabItemClicked(MenuItem menuItem) {
onMultipleItemAction(menuItem, new ArrayList<>(checked));
cab.finish();
unCheckAll();
if (menuItem.getItemId() == R.id.action_multi_select_adapter_check_all) {
checkAll();
} else {
onMultipleItemAction(menuItem, new ArrayList<>(checked));
cab.finish();
clearChecked();
}
return true;
}
@Override
public boolean onCabFinished(MaterialCab materialCab) {
unCheckAll();
clearChecked();
return true;
}
@ -97,6 +115,7 @@ public abstract class AbsMultiSelectAdapter<VH extends RecyclerView.ViewHolder,
return object.toString();
}
@Nullable
protected abstract I getIdentifier(int position);
protected abstract void onMultipleItemAction(MenuItem menuItem, ArrayList<I> selection);

View file

@ -53,10 +53,11 @@ public abstract class AbsOffsetSongAdapter extends SongAdapter {
return super.getItemId(position);
}
@Nullable
@Override
protected Song getIdentifier(int position) {
position--;
if (position < 0) return Song.EMPTY_SONG;
if (position < 0) return null;
return super.getIdentifier(position);
}
@ -87,7 +88,8 @@ public abstract class AbsOffsetSongAdapter extends SongAdapter {
@Override
protected Song getSong() {
if (getItemViewType() == OFFSET_ITEM) return Song.EMPTY_SONG;
if (getItemViewType() == OFFSET_ITEM)
return Song.EMPTY_SONG; // could also return null, just to be safe return empty song
return dataSet.get(getAdapterPosition() - 1);
}

View file

@ -33,7 +33,7 @@ public class OrderablePlaylistSongAdapter extends PlaylistSongAdapter implements
public OrderablePlaylistSongAdapter(@NonNull AppCompatActivity activity, @NonNull ArrayList<PlaylistSong> dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder, @Nullable OnMoveItemListener onMoveItemListener) {
super(activity, (ArrayList<Song>) (List) dataSet, itemLayoutRes, usePalette, cabHolder);
overrideMultiSelectMenuRes(R.menu.menu_playlists_songs_selection);
setMultiSelectMenuRes(R.menu.menu_playlists_songs_selection);
this.onMoveItemListener = onMoveItemListener;
}

View file

@ -26,7 +26,7 @@ public class PlaylistSongAdapter extends AbsOffsetSongAdapter {
public PlaylistSongAdapter(AppCompatActivity activity, @NonNull ArrayList<Song> dataSet, @LayoutRes int itemLayoutRes, boolean usePalette, @Nullable CabHolder cabHolder) {
super(activity, dataSet, itemLayoutRes, usePalette, cabHolder, false);
overrideMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection);
setMultiSelectMenuRes(R.menu.menu_cannot_delete_single_songs_playlist_songs_selection);
}
@Override

View file

@ -203,7 +203,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
sectionName = dataSet.get(position).artistName;
break;
case SortOrder.SongSortOrder.SONG_YEAR:
return Integer.toString(dataSet.get(position).year);
return MusicUtil.getYearString(dataSet.get(position).year);
}
return MusicUtil.getSectionName(sectionName);

View file

@ -45,7 +45,7 @@ public class AudioFileCoverFetcher implements DataFetcher<InputStream> {
}
}
private static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg"};
private static final String[] FALLBACKS = {"cover.jpg", "album.jpg", "folder.jpg", "cover.png", "album.png", "folder.png"};
private InputStream fallback(String path) throws FileNotFoundException {
File parent = new File(path).getParentFile();

View file

@ -1,20 +1,20 @@
package com.kabouzeid.gramophone.helper.menu;
import android.annotation.SuppressLint;
import android.app.Activity;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.NonNull;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.widget.Toast;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog;
import com.kabouzeid.gramophone.dialogs.RenamePlaylistDialog;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.loader.PlaylistSongLoader;
import com.kabouzeid.gramophone.misc.WeakContextAsyncTask;
import com.kabouzeid.gramophone.model.AbsCustomPlaylist;
import com.kabouzeid.gramophone.model.Playlist;
import com.kabouzeid.gramophone.model.Song;
@ -48,34 +48,7 @@ public class PlaylistMenuHelper {
DeletePlaylistDialog.create(playlist).show(activity.getSupportFragmentManager(), "DELETE_PLAYLIST");
return true;
case R.id.action_save_playlist:
@SuppressLint("ShowToast")
final Toast toast = Toast.makeText(activity, R.string.saving_to_file, Toast.LENGTH_SHORT);
new AsyncTask<Context, Void, String>() {
@Override
protected void onPreExecute() {
super.onPreExecute();
toast.show();
}
@Override
protected String doInBackground(Context... params) {
try {
return String.format(params[0].getString(R.string.saved_playlist_to), PlaylistsUtil.savePlaylist(params[0], playlist));
} catch (IOException e) {
e.printStackTrace();
return String.format(params[0].getString(R.string.failed_to_save_playlist), e);
}
}
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
if (toast != null) {
toast.setText(string);
toast.show();
}
}
}.execute(activity.getApplicationContext());
new SavePlaylistAsyncTask(activity).execute(playlist);
return true;
}
return false;
@ -87,4 +60,30 @@ public class PlaylistMenuHelper {
((AbsCustomPlaylist) playlist).getSongs(activity) :
PlaylistSongLoader.getPlaylistSongList(activity, playlist.id);
}
private static class SavePlaylistAsyncTask extends WeakContextAsyncTask<Playlist, String, String> {
public SavePlaylistAsyncTask(Context context) {
super(context);
}
@Override
protected String doInBackground(Playlist... params) {
try {
return String.format(App.getInstance().getApplicationContext().getString(R.string.saved_playlist_to), PlaylistsUtil.savePlaylist(App.getInstance().getApplicationContext(), params[0]));
} catch (IOException e) {
e.printStackTrace();
return String.format(App.getInstance().getApplicationContext().getString(R.string.failed_to_save_playlist), e);
}
}
@Override
protected void onPostExecute(String string) {
super.onPostExecute(string);
Context context = getContext();
if (context != null) {
Toast.makeText(context, string, Toast.LENGTH_LONG).show();
}
}
}
}

View file

@ -2,7 +2,6 @@ package com.kabouzeid.gramophone.misc;
import android.app.Dialog;
import android.content.Context;
import android.os.AsyncTask;
import android.os.Handler;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
@ -12,9 +11,8 @@ import java.lang.ref.WeakReference;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class DialogAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
public abstract class DialogAsyncTask<Params, Progress, Result> extends WeakContextAsyncTask<Params, Progress, Result> {
private final int delay;
private WeakReference<Context> contextWeakReference;
private WeakReference<Dialog> dialogWeakReference;
private boolean supposedToBeDismissed;
@ -24,8 +22,8 @@ public abstract class DialogAsyncTask<Params, Progress, Result> extends AsyncTas
}
public DialogAsyncTask(Context context, int showDelay) {
super(context);
this.delay = showDelay;
contextWeakReference = new WeakReference<>(context);
dialogWeakReference = new WeakReference<>(null);
}
@ -62,11 +60,6 @@ public abstract class DialogAsyncTask<Params, Progress, Result> extends AsyncTas
protected void onProgressUpdate(@NonNull Dialog dialog, Progress... values) {
}
@Nullable
protected Context getContext() {
return contextWeakReference.get();
}
@Nullable
protected Dialog getDialog() {
return dialogWeakReference.get();

View file

@ -0,0 +1,23 @@
package com.kabouzeid.gramophone.misc;
import android.content.Context;
import android.os.AsyncTask;
import android.support.annotation.Nullable;
import java.lang.ref.WeakReference;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class WeakContextAsyncTask<Params, Progress, Result> extends AsyncTask<Params, Progress, Result> {
private WeakReference<Context> contextWeakReference;
public WeakContextAsyncTask(Context context) {
contextWeakReference = new WeakReference<>(context);
}
@Nullable
protected Context getContext() {
return contextWeakReference.get();
}
}

View file

@ -418,7 +418,7 @@ public class AlbumDetailActivity extends AbsSlidingMusicPanelActivity implements
artistTextView.setText(album.getArtistName());
songCountTextView.setText(MusicUtil.getSongCountString(this, album.getSongCount()));
durationTextView.setText(MusicUtil.getReadableDurationString(MusicUtil.getTotalDuration(this, album.songs)));
albumYearTextView.setText(album.getYear() > 0 ? String.valueOf(album.getYear()) : "-");
albumYearTextView.setText(MusicUtil.getYearString(album.getYear()));
adapter.swapDataSet(album.songs);
}

View file

@ -188,7 +188,6 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
inflater.inflate(R.menu.menu_main, menu);
if (isPlaylistPage()) {
menu.add(0, R.id.action_new_playlist, 0, R.string.new_playlist_title);
menu.add(0, R.id.action_export_playlists, 1, R.string.export_playlists);
}
Fragment currentFragment = getCurrentFragment();
if (currentFragment instanceof AbsLibraryPagerRecyclerViewCustomGridSizeFragment && currentFragment.isAdded()) {
@ -249,9 +248,6 @@ public class LibraryFragment extends AbsMainActivityFragment implements CabHolde
case R.id.action_new_playlist:
CreatePlaylistDialog.create().show(getChildFragmentManager(), "CREATE_PLAYLIST");
return true;
case R.id.action_export_playlists:
// TODO
return true;
case R.id.action_search:
startActivity(new Intent(getActivity(), SearchActivity.class));
return true;

View file

@ -139,6 +139,11 @@ public class MusicUtil {
return albumCount + " " + albumString;
}
@NonNull
public static String getYearString(int year) {
return year > 0 ? String.valueOf(year) : "-";
}
public static long getTotalDuration(@NonNull final Context context, @NonNull List<Song> songs) {
long duration = 0;
for (int i = 0; i < songs.size(); i++) {