package com.alium.orin.ui.fragments.player.flat; import android.animation.Animator; import android.animation.AnimatorSet; import android.app.Activity; import android.graphics.Color; import android.graphics.PorterDuff; import android.graphics.drawable.Drawable; import android.os.AsyncTask; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.annotation.Nullable; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.text.TextUtils; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.ImageView; import android.widget.TextView; import com.alium.orin.adapter.base.MediaEntryViewHolder; import com.alium.orin.dialogs.LyricsDialog; import com.alium.orin.dialogs.SongShareDialog; import com.alium.orin.helper.MusicPlayerRemote; import com.alium.orin.model.Song; import com.alium.orin.ui.activities.base.AbsSlidingMusicPanelActivity; import com.alium.orin.ui.fragments.player.AbsPlayerFragment; import com.alium.orin.util.MusicUtil; import com.alium.orin.util.ViewUtil; import com.alium.orin.views.WidthFitSquareLayout; import com.h6ah4i.android.widget.advrecyclerview.animator.GeneralItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.animator.RefactoredDefaultItemAnimator; import com.h6ah4i.android.widget.advrecyclerview.draggable.RecyclerViewDragDropManager; import com.h6ah4i.android.widget.advrecyclerview.utils.WrapperAdapterUtils; import com.kabouzeid.appthemehelper.ThemeStore; import com.kabouzeid.appthemehelper.util.ATHUtil; import com.kabouzeid.appthemehelper.util.ColorUtil; import com.kabouzeid.appthemehelper.util.ToolbarContentTintHelper; import com.alium.orin.R; import com.alium.orin.adapter.song.PlayingQueueAdapter; import com.alium.orin.helper.menu.SongMenuHelper; import com.alium.orin.ui.fragments.player.PlayerAlbumCoverFragment; import com.alium.orin.util.Util; import com.sothree.slidinguppanel.SlidingUpPanelLayout; import org.jaudiotagger.audio.AudioFileIO; import org.jaudiotagger.tag.FieldKey; import java.io.File; import butterknife.BindView; import butterknife.ButterKnife; import butterknife.Unbinder; public class FlatPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks, SlidingUpPanelLayout.PanelSlideListener { public static final String TAG = FlatPlayerFragment.class.getSimpleName(); private Unbinder unbinder; @BindView(R.id.player_status_bar) View playerStatusBar; @BindView(R.id.player_toolbar) Toolbar toolbar; @Nullable @BindView(R.id.player_sliding_layout) SlidingUpPanelLayout slidingUpPanelLayout; @BindView(R.id.player_recycler_view) RecyclerView recyclerView; @BindView(R.id.player_queue_sub_header) TextView playerQueueSubHeader; private int lastColor; private FlatPlayerPlaybackControlsFragment playbackControlsFragment; private PlayerAlbumCoverFragment playerAlbumCoverFragment; private LinearLayoutManager layoutManager; private PlayingQueueAdapter playingQueueAdapter; private RecyclerView.Adapter wrappedAdapter; private RecyclerViewDragDropManager recyclerViewDragDropManager; private AsyncTask updateIsFavoriteTask; private AsyncTask updateLyricsAsyncTask; private LyricsDialog.LyricInfo lyricsInfo; private Impl impl; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { if (Util.isLandscape(getResources())) { impl = new LandscapeImpl(this); } else { impl = new PortraitImpl(this); } View view = inflater.inflate(R.layout.fragment_flat_player, container, false); unbinder = ButterKnife.bind(this, view); return view; } @Override public void onViewCreated(final View view, Bundle savedInstanceState) { super.onViewCreated(view, savedInstanceState); impl.init(); setUpPlayerToolbar(); setUpSubFragments(); setUpRecyclerView(); if (slidingUpPanelLayout != null) { slidingUpPanelLayout.addPanelSlideListener(this); slidingUpPanelLayout.setAntiDragView(view.findViewById(R.id.draggable_area)); } view.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { view.getViewTreeObserver().removeOnGlobalLayoutListener(this); impl.setUpPanelAndAlbumCoverHeight(); } }); } @Override public void onDestroyView() { if (slidingUpPanelLayout != null) { slidingUpPanelLayout.removePanelSlideListener(this); } if (recyclerViewDragDropManager != null) { recyclerViewDragDropManager.release(); recyclerViewDragDropManager = null; } if (recyclerView != null) { recyclerView.setItemAnimator(null); recyclerView.setAdapter(null); recyclerView = null; } if (wrappedAdapter != null) { WrapperAdapterUtils.releaseAll(wrappedAdapter); wrappedAdapter = null; } playingQueueAdapter = null; layoutManager = null; super.onDestroyView(); unbinder.unbind(); } @Override public void onPause() { if (recyclerViewDragDropManager != null) { recyclerViewDragDropManager.cancelDrag(); } super.onPause(); } @Override public void onServiceConnected() { updateQueue(); updateCurrentSong(); updateIsFavorite(); updateLyrics(); } @Override public void onPlayingMetaChanged() { updateCurrentSong(); updateIsFavorite(); updateQueuePosition(); updateLyrics(); } @Override public void onQueueChanged() { updateQueue(); } @Override public void onMediaStoreChanged() { updateQueue(); } private void updateQueue() { playingQueueAdapter.swapDataSet(MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition()); playerQueueSubHeader.setText(getUpNextAndQueueTime()); if (slidingUpPanelLayout == null || slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { resetToCurrentPosition(); } } private void updateQueuePosition() { playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition()); playerQueueSubHeader.setText(getUpNextAndQueueTime()); if (slidingUpPanelLayout == null || slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { resetToCurrentPosition(); } } @SuppressWarnings("ConstantConditions") private void updateCurrentSong() { impl.updateCurrentSong(MusicPlayerRemote.getCurrentSong()); } private void setUpSubFragments() { playbackControlsFragment = (FlatPlayerPlaybackControlsFragment) getChildFragmentManager().findFragmentById(R.id.playback_controls_fragment); playerAlbumCoverFragment = (PlayerAlbumCoverFragment) getChildFragmentManager().findFragmentById(R.id.player_album_cover_fragment); playerAlbumCoverFragment.setCallbacks(this); } private void setUpPlayerToolbar() { toolbar.inflateMenu(R.menu.menu_player); toolbar.setNavigationIcon(R.drawable.ic_close_white_24dp); toolbar.setNavigationOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { getActivity().onBackPressed(); } }); toolbar.setOnMenuItemClickListener(this); } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_show_lyrics: if (lyricsInfo != null) LyricsDialog.create(lyricsInfo).show(getFragmentManager(), "LYRICS"); return true; } return super.onMenuItemClick(item); } private void setUpRecyclerView() { recyclerViewDragDropManager = new RecyclerViewDragDropManager(); final GeneralItemAnimator animator = new RefactoredDefaultItemAnimator(); playingQueueAdapter = new PlayingQueueAdapter( ((AppCompatActivity) getActivity()), MusicPlayerRemote.getPlayingQueue(), MusicPlayerRemote.getPosition(), R.layout.item_list, false, null); wrappedAdapter = recyclerViewDragDropManager.createWrappedAdapter(playingQueueAdapter); layoutManager = new LinearLayoutManager(getActivity()); recyclerView.setLayoutManager(layoutManager); recyclerView.setAdapter(wrappedAdapter); recyclerView.setItemAnimator(animator); recyclerViewDragDropManager.attachRecyclerView(recyclerView); layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); } private void updateIsFavorite() { if (updateIsFavoriteTask != null) updateIsFavoriteTask.cancel(false); updateIsFavoriteTask = new AsyncTask<Song, Void, Boolean>() { @Override protected Boolean doInBackground(Song... params) { Activity activity = getActivity(); if (activity != null) { return MusicUtil.isFavorite(getActivity(), params[0]); } else { cancel(false); return null; } } @Override protected void onPostExecute(Boolean isFavorite) { Activity activity = getActivity(); if (activity != null) { int res = isFavorite ? R.drawable.ic_favorite_white_24dp : R.drawable.ic_favorite_border_white_24dp; int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); Drawable drawable = Util.getTintedVectorDrawable(activity, res, color); toolbar.getMenu().findItem(R.id.action_toggle_favorite) .setIcon(drawable) .setTitle(isFavorite ? getString(R.string.action_remove_from_favorites) : getString(R.string.action_add_to_favorites)); } } }.execute(MusicPlayerRemote.getCurrentSong()); } private void updateLyrics() { if (updateLyricsAsyncTask != null) updateLyricsAsyncTask.cancel(false); final Song song = MusicPlayerRemote.getCurrentSong(); updateLyricsAsyncTask = new AsyncTask<Void, Void, String>() { @Override protected void onPreExecute() { super.onPreExecute(); lyricsInfo = null; toolbar.getMenu().removeItem(R.id.action_show_lyrics); } @Override protected String doInBackground(Void... params) { try { return AudioFileIO.read(new File(song.data)).getTagOrCreateDefault().getFirst(FieldKey.LYRICS); } catch (Exception e) { e.printStackTrace(); cancel(false); return null; } } @Override protected void onPostExecute(String lyrics) { super.onPostExecute(lyrics); if (TextUtils.isEmpty(lyrics)) { lyricsInfo = null; if (toolbar != null) { toolbar.getMenu().removeItem(R.id.action_show_lyrics); } } else { lyricsInfo = new LyricsDialog.LyricInfo(song.title, lyrics); Activity activity = getActivity(); if (toolbar != null && activity != null) if (toolbar.getMenu().findItem(R.id.action_show_lyrics) == null) { int color = ToolbarContentTintHelper.toolbarContentColor(activity, Color.TRANSPARENT); Drawable drawable = Util.getTintedVectorDrawable(activity, R.drawable.ic_comment_text_outline_white_24dp, color); toolbar.getMenu() .add(Menu.NONE, R.id.action_show_lyrics, Menu.NONE, R.string.action_show_lyrics) .setIcon(drawable) .setShowAsAction(MenuItem.SHOW_AS_ACTION_ALWAYS); } } } @Override protected void onCancelled(String s) { onPostExecute(null); } }.execute(); } @Override @ColorInt public int getPaletteColor() { return lastColor; } private void animateColorChange(final int newColor) { impl.animateColorChange(newColor); lastColor = newColor; } @Override protected void toggleFavorite(Song song) { super.toggleFavorite(song); if (song.id == MusicPlayerRemote.getCurrentSong().id) { if (MusicUtil.isFavorite(getActivity(), song)) { playerAlbumCoverFragment.showHeartAnimation(); } updateIsFavorite(); } } @Override public void onShow() { playbackControlsFragment.show(); } @Override public void onHide() { playbackControlsFragment.hide(); onBackPressed(); } @Override public boolean onBackPressed() { boolean wasExpanded = false; if (slidingUpPanelLayout != null) { wasExpanded = slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED; slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); } return wasExpanded; } @Override public void onColorChanged(int color) { animateColorChange(color); playbackControlsFragment.setDark(ColorUtil.isColorLight(color)); getCallbacks().onPaletteColorChanged(); } @Override public void onFavoriteToggled() { toggleFavorite(MusicPlayerRemote.getCurrentSong()); } @Override public void onPanelSlide(View view, float slide) { } @Override public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) { switch (newState) { case COLLAPSED: onPanelCollapsed(panel); break; case ANCHORED: //noinspection ConstantConditions slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); // this fixes a bug where the panel would get stuck for some reason break; } } public void onPanelCollapsed(View panel) { resetToCurrentPosition(); } private void resetToCurrentPosition() { recyclerView.stopScroll(); layoutManager.scrollToPositionWithOffset(MusicPlayerRemote.getPosition() + 1, 0); } interface Impl { void init(); void updateCurrentSong(Song song); void animateColorChange(final int newColor); void setUpPanelAndAlbumCoverHeight(); } private static abstract class BaseImpl implements Impl { protected FlatPlayerFragment fragment; public BaseImpl(FlatPlayerFragment fragment) { this.fragment = fragment; } public AnimatorSet createDefaultColorChangeAnimatorSet(int newColor) { Animator backgroundAnimator = ViewUtil.createBackgroundColorTransition(fragment.playbackControlsFragment.getView(), fragment.lastColor, newColor); Animator statusBarAnimator = ViewUtil.createBackgroundColorTransition(fragment.playerStatusBar, fragment.lastColor, newColor); AnimatorSet animatorSet = new AnimatorSet(); animatorSet.playTogether(backgroundAnimator, statusBarAnimator); if (!ATHUtil.isWindowBackgroundDark(fragment.getActivity())) { int adjustedLastColor = ColorUtil.isColorLight(fragment.lastColor) ? ColorUtil.darkenColor(fragment.lastColor) : fragment.lastColor; int adjustedNewColor = ColorUtil.isColorLight(newColor) ? ColorUtil.darkenColor(newColor) : newColor; Animator subHeaderAnimator = ViewUtil.createTextColorTransition(fragment.playerQueueSubHeader, adjustedLastColor, adjustedNewColor); animatorSet.play(subHeaderAnimator); } animatorSet.setDuration(ViewUtil.PHONOGRAPH_ANIM_TIME); return animatorSet; } @Override public void animateColorChange(int newColor) { if (ATHUtil.isWindowBackgroundDark(fragment.getActivity())) { fragment.playerQueueSubHeader.setTextColor(ThemeStore.textColorSecondary(fragment.getActivity())); } } } @SuppressWarnings("ConstantConditions") private static class PortraitImpl extends BaseImpl { MediaEntryViewHolder currentSongViewHolder; Song currentSong = Song.EMPTY_SONG; public PortraitImpl(FlatPlayerFragment fragment) { super(fragment); } @Override public void init() { currentSongViewHolder = new MediaEntryViewHolder(fragment.getView().findViewById(R.id.current_song)); currentSongViewHolder.separator.setVisibility(View.VISIBLE); currentSongViewHolder.shortSeparator.setVisibility(View.GONE); currentSongViewHolder.image.setScaleType(ImageView.ScaleType.CENTER); currentSongViewHolder.image.setColorFilter(ATHUtil.resolveColor(fragment.getActivity(), R.attr.iconColor, ThemeStore.textColorSecondary(fragment.getActivity())), PorterDuff.Mode.SRC_IN); currentSongViewHolder.image.setImageResource(R.drawable.ic_volume_up_white_24dp); currentSongViewHolder.itemView.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View v) { // toggle the panel if (fragment.slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) { fragment.slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.EXPANDED); } else if (fragment.slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.EXPANDED) { fragment.slidingUpPanelLayout.setPanelState(SlidingUpPanelLayout.PanelState.COLLAPSED); } } }); currentSongViewHolder.menu.setOnClickListener(new SongMenuHelper.OnClickSongMenu((AppCompatActivity) fragment.getActivity()) { @Override public Song getSong() { return currentSong; } public int getMenuRes() { return R.menu.menu_item_playing_queue_song; } @Override public boolean onMenuItemClick(MenuItem item) { switch (item.getItemId()) { case R.id.action_remove_from_playing_queue: MusicPlayerRemote.removeFromQueue(MusicPlayerRemote.getPosition()); return true; case R.id.action_share: SongShareDialog.create(getSong()).show(fragment.getFragmentManager(), "SONG_SHARE_DIALOG"); return true; } return super.onMenuItemClick(item); } }); } @Override public void setUpPanelAndAlbumCoverHeight() { WidthFitSquareLayout albumCoverContainer = (WidthFitSquareLayout) fragment.getView().findViewById(R.id.album_cover_container); final int availablePanelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.getView().findViewById(R.id.player_content).getHeight(); final int minPanelHeight = (int) ViewUtil.convertDpToPixel(8 + 72 + 24, fragment.getResources()) + fragment.getResources().getDimensionPixelSize(R.dimen.progress_container_height) + fragment.getResources().getDimensionPixelSize(R.dimen.media_controller_container_height); if (availablePanelHeight < minPanelHeight) { albumCoverContainer.getLayoutParams().height = albumCoverContainer.getHeight() - (minPanelHeight - availablePanelHeight); albumCoverContainer.forceSquare(false); } fragment.slidingUpPanelLayout.setPanelHeight(Math.max(minPanelHeight, availablePanelHeight)); ((AbsSlidingMusicPanelActivity) fragment.getActivity()).setAntiDragView(fragment.slidingUpPanelLayout.findViewById(R.id.player_panel)); } @Override public void updateCurrentSong(Song song) { currentSong = song; currentSongViewHolder.title.setText(song.title); currentSongViewHolder.text.setText(song.artistName); } @Override public void animateColorChange(int newColor) { super.animateColorChange(newColor); createDefaultColorChangeAnimatorSet(newColor).start(); } } @SuppressWarnings("ConstantConditions") private static class LandscapeImpl extends BaseImpl { public LandscapeImpl(FlatPlayerFragment fragment) { super(fragment); } @Override public void init() { } @Override public void setUpPanelAndAlbumCoverHeight() { ((AbsSlidingMusicPanelActivity) fragment.getActivity()).setAntiDragView(fragment.getView().findViewById(R.id.player_panel)); } @Override public void updateCurrentSong(Song song) { fragment.toolbar.setTitle(song.title); fragment.toolbar.setSubtitle(song.artistName); } @Override public void animateColorChange(int newColor) { super.animateColorChange(newColor); AnimatorSet animatorSet = createDefaultColorChangeAnimatorSet(newColor); animatorSet.play(ViewUtil.createBackgroundColorTransition(fragment.toolbar, fragment.lastColor, newColor)); animatorSet.start(); } } }