package com.simplecity.amp_library.ui.fragments; import android.annotation.SuppressLint; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.SharedPreferences; import android.os.Bundle; import android.preference.PreferenceManager; import android.support.v7.app.AppCompatActivity; import android.support.v7.view.ActionMode; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.PopupMenu; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.SubMenu; import android.view.View; import android.view.ViewGroup; import com.annimon.stream.Collectors; import com.annimon.stream.Stream; import com.bignerdranch.android.multiselector.ModalMultiSelectorCallback; import com.bignerdranch.android.multiselector.MultiSelector; import com.bumptech.glide.Glide; import com.bumptech.glide.RequestManager; import com.simplecity.amp_library.R; import com.simplecity.amp_library.model.AdaptableItem; import com.simplecity.amp_library.model.Album; import com.simplecity.amp_library.model.Playlist; import com.simplecity.amp_library.model.Song; import com.simplecity.amp_library.ui.adapters.AlbumAdapter; import com.simplecity.amp_library.ui.modelviews.AlbumView; import com.simplecity.amp_library.ui.modelviews.EmptyView; import com.simplecity.amp_library.ui.modelviews.ViewType; import com.simplecity.amp_library.ui.recyclerview.GridDividerDecoration; import com.simplecity.amp_library.utils.ColorUtils; import com.simplecity.amp_library.utils.DataManager; import com.simplecity.amp_library.utils.DialogUtils; import com.simplecity.amp_library.utils.MenuUtils; import com.simplecity.amp_library.utils.MusicUtils; import com.simplecity.amp_library.utils.PermissionUtils; import com.simplecity.amp_library.utils.PlaylistUtils; import com.simplecity.amp_library.utils.SettingsManager; import com.simplecity.amp_library.utils.ShuttleUtils; import com.simplecity.amp_library.utils.SortManager; import com.simplecity.amp_library.utils.ThemeUtils; import com.simplecityapps.recyclerview_fastscroll.views.FastScrollRecyclerView; import java.util.Collections; import java.util.List; import rx.Observable; import rx.Subscription; import rx.android.schedulers.AndroidSchedulers; public class AlbumFragment extends BaseFragment implements MusicUtils.Defs, RecyclerView.RecyclerListener, AlbumAdapter.AlbumListener { public interface AlbumClickListener { void onItemClicked(Album album, View transitionView); } private final static String TAG = "AlbumFragment"; private static final String ARG_PAGE_TITLE = "page_title"; private static final int MENU_GRID_SIZE = 100; private static final int MENU_GROUP_GRID = 1; private SharedPreferences prefs; private AlbumClickListener albumClickListener; private FastScrollRecyclerView recyclerView; private GridLayoutManager layoutManager; AlbumAdapter albumAdapter; MultiSelector multiSelector = new MultiSelector(); ActionMode actionMode; boolean inActionMode = false; private BroadcastReceiver receiver; private SharedPreferences.OnSharedPreferenceChangeListener sharedPreferenceChangeListener; private boolean sortOrderChanged = false; private Subscription subscription; private RequestManager requestManager; public AlbumFragment() { } public static AlbumFragment newInstance(String pageTitle) { AlbumFragment albumFragment = new AlbumFragment(); Bundle args = new Bundle(); args.putString(ARG_PAGE_TITLE, pageTitle); albumFragment.setArguments(args); return albumFragment; } @Override public void onAttach(Context context) { super.onAttach(context); albumClickListener = (AlbumClickListener) getActivity(); } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setHasOptionsMenu(true); albumAdapter = new AlbumAdapter(); albumAdapter.setListener(this); prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity()); receiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (intent.getAction() != null && intent.getAction().equals("restartLoader")) { refreshAdapterItems(); } } }; sharedPreferenceChangeListener = (sharedPreferences, key) -> { if (key.equals("pref_theme_highlight_color") || key.equals("pref_theme_accent_color") || key.equals("pref_theme_white_accent")) { themeUIComponents(); } else if (key.equals("albumWhitelist")) { refreshAdapterItems(); } }; prefs.registerOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); if (requestManager == null) { requestManager = Glide.with(this); } } public void themeUIComponents() { ThemeUtils.themeRecyclerView(recyclerView); recyclerView.setThumbColor(ColorUtils.getAccentColor()); recyclerView.setPopupBgColor(ColorUtils.getAccentColor()); recyclerView.setPopupTextColor(ColorUtils.getAccentColorSensitiveTextColor(getContext())); recyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrollStateChanged(RecyclerView recyclerView, int newState) { ThemeUtils.themeRecyclerView(recyclerView); super.onScrollStateChanged(recyclerView, newState); } }); } @SuppressLint("NewApi") @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (recyclerView == null) { int spanCount = SettingsManager.getInstance().getAlbumColumnCount(getResources()); layoutManager = new GridLayoutManager(getContext(), spanCount); layoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { @Override public int getSpanSize(int position) { if (albumAdapter.items.get(position) instanceof EmptyView) { return spanCount; } return 1; } }); recyclerView = (FastScrollRecyclerView) inflater.inflate(R.layout.fragment_recycler, container, false); recyclerView.setLayoutManager(layoutManager); recyclerView.addItemDecoration(new GridDividerDecoration(getResources(), 4, true)); recyclerView.setRecyclerListener(this); recyclerView.setAdapter(albumAdapter); actionMode = null; themeUIComponents(); } return recyclerView; } @Override public void onPause() { if (receiver != null) { getActivity().unregisterReceiver(receiver); } if (subscription != null) { subscription.unsubscribe(); } super.onPause(); } @Override public void onResume() { super.onResume(); IntentFilter filter = new IntentFilter(); filter.addAction("restartLoader"); getActivity().registerReceiver(receiver, filter); refreshAdapterItems(); } @Override public void onDestroy() { prefs.unregisterOnSharedPreferenceChangeListener(sharedPreferenceChangeListener); super.onDestroy(); } void refreshAdapterItems() { PermissionUtils.RequestStoragePermissions(() -> { if (getActivity() != null && isAdded()) { int albumDisplayType = SettingsManager.getInstance().getAlbumDisplayType(); boolean ascending = SortManager.getInstance().getAlbumsAscending(); subscription = DataManager.getInstance().getAlbumsRelay() .flatMap(albums -> { //Sort SortManager.getInstance().sortAlbums(albums); //Reverse if required if (!ascending) { Collections.reverse(albums); } return Observable.from(albums) .map(album -> (AdaptableItem) new AlbumView(album, albumDisplayType, requestManager, multiSelector)) .toList(); }) .observeOn(AndroidSchedulers.mainThread()) .subscribe(items -> { if (items.isEmpty()) { albumAdapter.setEmpty(new EmptyView(R.string.empty_albums)); } else { albumAdapter.setItems(items); } //Move the RV back to the top if we've had a sort order change. if (sortOrderChanged) { recyclerView.scrollToPosition(0); } sortOrderChanged = false; }); } }); } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { super.onCreateOptionsMenu(menu, inflater); inflater.inflate(R.menu.menu_sort_albums, menu); inflater.inflate(R.menu.menu_view_as, menu); menu.addSubMenu(0, MENU_GRID_SIZE, 0, R.string.menu_grid_size); SubMenu subMenu = menu.findItem(MENU_GRID_SIZE).getSubMenu(); int[] columnRange = getResources().getIntArray(R.array.column_range); for (int i = 0; i < columnRange.length; i++) { subMenu.add(MENU_GROUP_GRID, columnRange[i] + 1000, i, String.valueOf(columnRange[i])); } subMenu.setGroupCheckable(MENU_GROUP_GRID, true, true); } @Override public void onPrepareOptionsMenu(Menu menu) { super.onPrepareOptionsMenu(menu); int sortOrder = SortManager.getInstance().getAlbumsSortOrder(); switch (sortOrder) { case SortManager.AlbumSort.DEFAULT: menu.findItem(R.id.sort_default).setChecked(true); break; case SortManager.AlbumSort.NAME: menu.findItem(R.id.sort_album_name).setChecked(true); break; case SortManager.AlbumSort.YEAR: menu.findItem(R.id.sort_album_year).setChecked(true); break; case SortManager.AlbumSort.ARTIST_NAME: menu.findItem(R.id.sort_album_artist_name).setChecked(true); break; } menu.findItem(R.id.sort_ascending).setChecked(SortManager.getInstance().getAlbumsAscending()); int displayType = SettingsManager.getInstance().getAlbumDisplayType(); switch (displayType) { case ViewType.ALBUM_LIST: menu.findItem(R.id.view_as_list).setChecked(true); break; case ViewType.ALBUM_GRID: menu.findItem(R.id.view_as_grid).setChecked(true); break; case ViewType.ALBUM_CARD: menu.findItem(R.id.view_as_grid_card).setChecked(true); break; case ViewType.ALBUM_PALETTE: menu.findItem(R.id.view_as_grid_palette).setChecked(true); break; } MenuItem gridMenuItem = menu.findItem(MENU_GRID_SIZE); if (displayType == ViewType.ALBUM_LIST) { gridMenuItem.setVisible(false); } else { gridMenuItem.setVisible(true); int columnCount = SettingsManager.getInstance().getAlbumColumnCount(getResources()); gridMenuItem.getSubMenu().findItem(columnCount + 1000).setChecked(true); } } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.sort_default: SortManager.getInstance().setAlbumsSortOrder(SortManager.AlbumSort.DEFAULT); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_album_name: SortManager.getInstance().setAlbumsSortOrder(SortManager.AlbumSort.NAME); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_album_year: SortManager.getInstance().setAlbumsSortOrder(SortManager.AlbumSort.YEAR); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_album_artist_name: SortManager.getInstance().setAlbumsSortOrder(SortManager.AlbumSort.ARTIST_NAME); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.sort_ascending: SortManager.getInstance().setAlbumsAscending(!item.isChecked()); sortOrderChanged = true; refreshAdapterItems(); break; case R.id.view_as_list: SettingsManager.getInstance().setAlbumDisplayType(ViewType.ALBUM_LIST); layoutManager.setSpanCount(getResources().getInteger(R.integer.list_num_columns)); albumAdapter.updateItemViewType(); albumAdapter.notifyItemRangeChanged(0, albumAdapter.getItemCount()); break; case R.id.view_as_grid: SettingsManager.getInstance().setAlbumDisplayType(ViewType.ALBUM_GRID); layoutManager.setSpanCount(SettingsManager.getInstance().getAlbumColumnCount(getResources())); albumAdapter.updateItemViewType(); albumAdapter.notifyItemRangeChanged(0, albumAdapter.getItemCount()); break; case R.id.view_as_grid_card: SettingsManager.getInstance().setAlbumDisplayType(ViewType.ALBUM_CARD); layoutManager.setSpanCount(SettingsManager.getInstance().getAlbumColumnCount(getResources())); albumAdapter.updateItemViewType(); albumAdapter.notifyItemRangeChanged(0, albumAdapter.getItemCount()); break; case R.id.view_as_grid_palette: SettingsManager.getInstance().setAlbumDisplayType(ViewType.ALBUM_PALETTE); layoutManager.setSpanCount(SettingsManager.getInstance().getAlbumColumnCount(getResources())); albumAdapter.updateItemViewType(); albumAdapter.notifyItemRangeChanged(0, albumAdapter.getItemCount()); break; } if (item.getGroupId() == MENU_GROUP_GRID) { SettingsManager.getInstance().setAlbumColumnCount(item.getItemId() - 1000); if (SettingsManager.getInstance().getAlbumDisplayType() != ViewType.ALBUM_LIST) { ((GridLayoutManager) recyclerView.getLayoutManager()).setSpanCount(SettingsManager.getInstance().getAlbumColumnCount(getResources())); albumAdapter.notifyItemRangeChanged(0, albumAdapter.getItemCount()); } } getActivity().supportInvalidateOptionsMenu(); return super.onOptionsItemSelected(item); } @Override public void onItemClick(View v, int position, Album album) { if (inActionMode) { multiSelector.setSelected(position, albumAdapter.getItemId(position), !multiSelector.isSelected(position, albumAdapter.getItemId(position))); if (multiSelector.getSelectedPositions().size() == 0) { if (actionMode != null) { actionMode.finish(); } } updateActionModeSelectionCount(); } else { albumClickListener.onItemClicked(album, v.findViewById(R.id.image)); } } @Override public void onOverflowClick(View v, int position, Album album) { PopupMenu menu = new PopupMenu(AlbumFragment.this.getActivity(), v); MenuUtils.addAlbumMenuOptions(getActivity(), menu); MenuUtils.addClickHandler((AppCompatActivity) getActivity(), menu, album); menu.show(); } @Override public void onLongClick(View v, int position, Album album) { if (inActionMode) { return; } if (multiSelector.getSelectedPositions().size() == 0) { actionMode = ((AppCompatActivity) getActivity()).startSupportActionMode(mActionModeCallback); inActionMode = true; } multiSelector.setSelected(position, albumAdapter.getItemId(position), !multiSelector.isSelected(position, albumAdapter.getItemId(position))); updateActionModeSelectionCount(); } @Override public void onViewRecycled(RecyclerView.ViewHolder holder) { if (holder.getAdapterPosition() != -1) { albumAdapter.items.get(holder.getAdapterPosition()).recycle(holder); } } private void updateActionModeSelectionCount() { if (actionMode != null && multiSelector != null) { actionMode.setTitle(getString(R.string.action_mode_selection_count, multiSelector.getSelectedPositions().size())); } } private ActionMode.Callback mActionModeCallback = new ModalMultiSelectorCallback(multiSelector) { @Override public boolean onCreateActionMode(ActionMode mode, Menu menu) { ThemeUtils.themeContextualActionBar(getActivity()); inActionMode = true; MenuInflater inflater = getActivity().getMenuInflater(); inflater.inflate(R.menu.context_menu_songs, menu); SubMenu sub = menu.getItem(0).getSubMenu(); PlaylistUtils.makePlaylistMenu(AlbumFragment.this.getActivity(), sub, ALBUM_FRAGMENT_GROUP_ID); return true; } @Override public boolean onActionItemClicked(final ActionMode mode, MenuItem menuItem) { List<Album> checkedAlbums = getCheckedAlbums(); if (checkedAlbums == null || checkedAlbums.size() == 0) { return true; } Observable<List<Song>> songsObservable = Observable.defer(() -> Observable.merge(Stream.of(checkedAlbums) .map(Album::getSongsObservable) .collect(Collectors.toList())) .reduce((songs, songs2) -> Stream.concat(Stream.of(songs), Stream.of(songs2)) .collect(Collectors.toList()))); switch (menuItem.getItemId()) { case NEW_PLAYLIST: songsObservable .observeOn(AndroidSchedulers.mainThread()) .subscribe(songs -> PlaylistUtils.createPlaylistDialog(getActivity(), songs)); return true; case PLAYLIST_SELECTED: songsObservable .observeOn(AndroidSchedulers.mainThread()) .subscribe(songs -> { Playlist playlist = (Playlist) menuItem.getIntent().getSerializableExtra(ShuttleUtils.ARG_PLAYLIST); PlaylistUtils.addToPlaylist(getContext(), playlist, songs); }); return true; case R.id.delete: { new DialogUtils.DeleteDialogBuilder() .context(getContext()) .singleMessageId(R.string.delete_album_artist_desc) .multipleMessage(R.string.delete_album_artist_desc_multiple) .itemNames(Stream.of(checkedAlbums) .map(album -> album.name) .collect(Collectors.toList())) .songsToDelete(songsObservable) .build() .show(); mode.finish(); return true; } case R.id.menu_add_to_queue: { songsObservable .observeOn(AndroidSchedulers.mainThread()) .subscribe(songs -> MusicUtils.addToQueue(getActivity(), songs)); break; } } return true; } @Override public void onDestroyActionMode(ActionMode actionMode) { super.onDestroyActionMode(actionMode); inActionMode = false; AlbumFragment.this.actionMode = null; multiSelector.clearSelections(); } }; List<Album> getCheckedAlbums() { return Stream.of(multiSelector.getSelectedPositions()) .map(i -> ((AlbumView) albumAdapter.items.get(i)).album) .collect(Collectors.toList()); } @Override protected String screenName() { return TAG; } }