content: package com.alium.orin.ui.fragments.player.card;
  
  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.Build;
  import android.os.Bundle;
  import android.support.annotation.ColorInt;
  import android.support.v7.app.AppCompatActivity;
  import android.support.v7.widget.CardView;
  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.ViewAnimationUtils;
  import android.view.ViewGroup;
  import android.view.ViewTreeObserver;
  import android.widget.ImageView;
  import android.widget.TextView;
  
  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.util.MusicUtil;
  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.base.MediaEntryViewHolder;
  import com.alium.orin.adapter.song.PlayingQueueAdapter;
  import com.alium.orin.dialogs.LyricsDialog;
  import com.alium.orin.helper.menu.SongMenuHelper;
  import com.alium.orin.ui.fragments.player.AbsPlayerFragment;
  import com.alium.orin.ui.fragments.player.PlayerAlbumCoverFragment;
  import com.alium.orin.util.Util;
  import com.alium.orin.util.ViewUtil;
  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 CardPlayerFragment extends AbsPlayerFragment implements PlayerAlbumCoverFragment.Callbacks, SlidingUpPanelLayout.PanelSlideListener {
      public static final String TAG = CardPlayerFragment.class.getSimpleName();
  
      private Unbinder unbinder;
  
      @BindView(R.id.player_toolbar)
      Toolbar toolbar;
      @BindView(R.id.player_sliding_layout)
      SlidingUpPanelLayout slidingUpPanelLayout;
      @BindView(R.id.player_recycler_view)
      RecyclerView recyclerView;
      @BindView(R.id.playing_queue_card)
      CardView playingQueueCard;
      @BindView(R.id.color_background)
      View colorBackground;
      @BindView(R.id.player_queue_sub_header)
      TextView playerQueueSubHeader;
  
      private int lastColor;
  
      private CardPlayerPlaybackControlsFragment 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_card_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();
  
          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();
              }
          });
  
          // for some reason the xml attribute doesn't get applied here.
          playingQueueCard.setCardBackgroundColor(ATHUtil.resolveColor(getActivity(), R.attr.cardBackgroundColor));
      }
  
      @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.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) {
              resetToCurrentPosition();
          }
      }
  
      private void updateQueuePosition() {
          playingQueueAdapter.setCurrent(MusicPlayerRemote.getPosition());
          playerQueueSubHeader.setText(getUpNextAndQueueTime());
          if (slidingUpPanelLayout.getPanelState() == SlidingUpPanelLayout.PanelState.COLLAPSED) {
              resetToCurrentPosition();
          }
      }
  
      @SuppressWarnings("ConstantConditions")
      private void updateCurrentSong() {
          impl.updateCurrentSong(MusicPlayerRemote.getCurrentSong());
      }
  
      private void setUpSubFragments() {
          playbackControlsFragment = (CardPlayerPlaybackControlsFragment) 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) {
          if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
              float density = getResources().getDisplayMetrics().density;
              playingQueueCard.setCardElevation((6 * slide + 2) * density);
              playbackControlsFragment.playPauseFab.setElevation((2 * Math.max(0, (1 - (slide * 16))) + 2) * density);
          }
      }
  
      @Override
      public void onPanelStateChanged(View panel, SlidingUpPanelLayout.PanelState previousState, SlidingUpPanelLayout.PanelState newState) {
          switch (newState) {
              case COLLAPSED:
                  onPanelCollapsed(panel);
                  break;
              case ANCHORED:
                  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 CardPlayerFragment fragment;
  
          public BaseImpl(CardPlayerFragment fragment) {
              this.fragment = fragment;
          }
  
          public AnimatorSet createDefaultColorChangeAnimatorSet(int newColor) {
              Animator backgroundAnimator;
              if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                  int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding);
                  //noinspection ConstantConditions
                  int x = (int) (fragment.playbackControlsFragment.playPauseFab.getX() + fragment.playbackControlsFragment.playPauseFab.getWidth() / 2 + fragment.playbackControlsFragment.getView().getX());
                  int y = (int) (topMargin + fragment.playbackControlsFragment.playPauseFab.getY() + fragment.playbackControlsFragment.playPauseFab.getHeight() / 2 + fragment.playbackControlsFragment.getView().getY());
                  float startRadius = Math.max(fragment.playbackControlsFragment.playPauseFab.getWidth() / 2, fragment.playbackControlsFragment.playPauseFab.getHeight() / 2);
                  float endRadius = Math.max(fragment.colorBackground.getWidth(), fragment.colorBackground.getHeight());
                  fragment.colorBackground.setBackgroundColor(newColor);
                  backgroundAnimator = ViewAnimationUtils.createCircularReveal(fragment.colorBackground, x, y, startRadius, endRadius);
              } else {
                  backgroundAnimator = ViewUtil.createBackgroundColorTransition(fragment.colorBackground, fragment.lastColor, newColor);
              }
  
              AnimatorSet animatorSet = new AnimatorSet();
  
              animatorSet.play(backgroundAnimator);
  
              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(CardPlayerFragment 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);
              int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding);
  
              final int availablePanelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.getView().findViewById(R.id.player_content).getHeight() + topMargin;
              final int minPanelHeight = (int) ViewUtil.convertDpToPixel(72 + 24, fragment.getResources()) + topMargin;
              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);
  
              fragment.slidingUpPanelLayout.setBackgroundColor(fragment.lastColor);
  
              createDefaultColorChangeAnimatorSet(newColor).start();
          }
      }
  
      @SuppressWarnings("ConstantConditions")
      private static class LandscapeImpl extends BaseImpl {
          public LandscapeImpl(CardPlayerFragment fragment) {
              super(fragment);
          }
  
          @Override
          public void init() {
  
          }
  
          @Override
          public void setUpPanelAndAlbumCoverHeight() {
              int topMargin = fragment.getResources().getDimensionPixelSize(R.dimen.status_bar_padding);
              int panelHeight = fragment.slidingUpPanelLayout.getHeight() - fragment.playbackControlsFragment.getView().getHeight() + topMargin;
              fragment.slidingUpPanelLayout.setPanelHeight(panelHeight);
  
              ((AbsSlidingMusicPanelActivity) fragment.getActivity()).setAntiDragView(fragment.slidingUpPanelLayout.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);
  
              fragment.slidingUpPanelLayout.setBackgroundColor(fragment.lastColor);
  
              AnimatorSet animatorSet = createDefaultColorChangeAnimatorSet(newColor);
              animatorSet.play(ViewUtil.createBackgroundColorTransition(fragment.toolbar, fragment.lastColor, newColor))
                      .with(ViewUtil.createBackgroundColorTransition(fragment.getView().findViewById(R.id.status_bar), ColorUtil.darkenColor(fragment.lastColor), ColorUtil.darkenColor(newColor)));
              animatorSet.start();
          }
      }
  }