package pasta.music.activities; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.app.ActivityManager; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.databinding.DataBindingUtil; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.drawable.Drawable; import android.os.Build; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.design.widget.BottomSheetBehavior; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.graphics.Palette; import android.support.v7.widget.AppCompatSeekBar; import android.support.v7.widget.GridLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.Toolbar; import android.util.TypedValue; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.widget.FrameLayout; import android.widget.ImageView; import android.widget.ProgressBar; import android.widget.SeekBar; import android.widget.TextView; import com.afollestad.async.Action; import com.bumptech.glide.Glide; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.request.animation.GlideAnimation; import com.bumptech.glide.request.target.GlideDrawableImageViewTarget; import java.util.ArrayList; import butterknife.Bind; import butterknife.ButterKnife; import butterknife.OnClick; import pasta.music.Pasta; import pasta.music.PlayerService; import pasta.music.R; import pasta.music.adapters.NowPlayingAdapter; import pasta.music.data.AlbumListData; import pasta.music.data.ArtistListData; import pasta.music.data.TrackListData; import pasta.music.dialogs.AddToPlaylistDialog; import pasta.music.utils.ImageUtils; import pasta.music.utils.PreferenceUtils; import pasta.music.utils.StaticUtils; import pasta.music.views.CustomImageView; public class PlayerActivity extends AppCompatActivity { @Bind(R.id.toolbar) Toolbar toolbar; @Bind(R.id.trackImage) CustomImageView art; @Nullable @Bind(R.id.backgroundImage) CustomImageView backgroundImage; @Bind(R.id.currentDuration) TextView currentDuration; @Bind(R.id.finalDuration) TextView finalDuration; @Bind(R.id.prevButton) CustomImageView prevButton; @Bind(R.id.playButton) CustomImageView playButton; @Bind(R.id.nextButton) CustomImageView nextButton; @Bind(R.id.bg) View bg; @Bind(R.id.seekBar) AppCompatSeekBar seekBar; @Bind(R.id.progressBar) ProgressBar progressBar; @Bind(R.id.title) TextView title; @Nullable @Bind(R.id.subtitle2) TextView subtitle2; @Bind(R.id.subtitle) TextView subtitle; @Bind(R.id.rv) RecyclerView rv; private ArrayList<TrackListData> trackList; private MenuItem fav; private boolean playing, playbarDragging; private int curPosition = -1; private TrackListData data; private String lastUri; private Drawable play, pause; private UpdateReceiver updateReceiver; private NowPlayingAdapter adapter; private boolean palette; private Pasta pasta; private Action action; @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); if (PreferenceUtils.isDarkTheme(this)) setTheme(R.style.AppTheme_Transparent_Dark); DataBindingUtil.setContentView(this, R.layout.activity_player); ButterKnife.bind(this); pasta = (Pasta) getApplicationContext(); pasta.setScreen(this); palette = PreferenceUtils.isPalette(this); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP && subtitle2 == null) { BottomSheetBehavior.from(rv).setBottomSheetCallback(new BottomSheetBehavior.BottomSheetCallback() { int scroll = 0; @Override public void onStateChanged(@NonNull View bottomSheet, int newState) { if (newState == BottomSheetBehavior.STATE_COLLAPSED) scroll = 0; } @Override public void onSlide(@NonNull View bottomSheet, float slideOffset) { int padding = (int) (StaticUtils.getStatusBarMargin(PlayerActivity.this) * (slideOffset < 0 ? slideOffset + 1 : slideOffset)); rv.setPadding(0, padding, 0, 0); rv.scrollBy(0, scroll - padding); scroll = padding; } }); } play = ImageUtils.getVectorDrawable(this, R.drawable.ic_play); pause = ImageUtils.getVectorDrawable(this, R.drawable.ic_pause); playButton.setImageDrawable(play); if (backgroundImage != null && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { TypedValue value = new TypedValue(); getTheme().resolveAttribute(android.R.attr.actionBarSize, value, true); FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(FrameLayout.LayoutParams.MATCH_PARENT, TypedValue.complexToDimensionPixelSize(value.data, getResources().getDisplayMetrics())); params.topMargin = StaticUtils.getStatusBarMargin(this); toolbar.setLayoutParams(params); } setLoading(true); setClickable(false); rv.setLayoutManager(new GridLayoutManager(this, 1)); adapter = new NowPlayingAdapter(this, trackList, 0); rv.setAdapter(adapter); seekBar.setOnSeekBarChangeListener(new SeekBar.OnSeekBarChangeListener() { @Override public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) { if (fromUser) StaticUtils.jumpToPositionInTrack(progress, seekBar.getContext()); } @Override public void onStartTrackingTouch(SeekBar seekBar) { playbarDragging = true; } @Override public void onStopTrackingTouch(SeekBar seekBar) { playbarDragging = false; } }); updateReceiver = new UpdateReceiver(); registerReceiver(updateReceiver, new IntentFilter(PlayerService.STATE_UPDATE)); setSupportActionBar(toolbar); getSupportActionBar().setDisplayHomeAsUpEnabled(true); getSupportActionBar().setHomeAsUpIndicator(R.drawable.ic_close); } @OnClick(R.id.prevButton) public void prev() { StaticUtils.previous(this); setLoading(true); setClickable(false); } @OnClick(R.id.playButton) public void toggle(ImageView v) { v.setImageDrawable(playing ? play : pause); StaticUtils.togglePlay(this); } @OnClick(R.id.nextButton) public void next() { StaticUtils.next(this); setLoading(true); setClickable(false); } @Override public void onResume() { super.onResume(); lastUri = null; trackList = null; if (updateReceiver != null) registerReceiver(updateReceiver, new IntentFilter(PlayerService.STATE_UPDATE)); } @Override public void onPause() { super.onPause(); unregisterReceiver(updateReceiver); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater menuInflater = getMenuInflater(); menuInflater.inflate(R.menu.menu_track, menu); fav = menu.findItem(R.id.action_fav); return super.onCreateOptionsMenu(menu); } @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case android.R.id.home: finish(); break; case R.id.action_fav: new Action<Boolean>() { @NonNull @Override public String id() { return "favTrack"; } @Nullable @Override protected Boolean run() throws InterruptedException { if (trackList == null || !pasta.toggleFavorite(trackList.get(curPosition))) { return null; } else return pasta.isFavorite(trackList.get(curPosition)); } @Override protected void done(@Nullable Boolean result) { if (result == null) { pasta.onError(PlayerActivity.this, "favorite track menu action"); return; } if (result) { item.setIcon(R.drawable.ic_fav); } else { item.setIcon(R.drawable.ic_unfav); } } }.execute(); break; case R.id.action_add: if (trackList != null) new AddToPlaylistDialog(this, trackList.get(curPosition)).show(); else pasta.onError(PlayerActivity.this, "add playlist dialog"); break; case R.id.action_album: new Action<AlbumListData>() { @NonNull @Override public String id() { return "gotoAlbum"; } @Nullable @Override protected AlbumListData run() throws InterruptedException { if (trackList != null) return pasta.getAlbum(trackList.get(curPosition).albumId); else return null; } @Override protected void done(@Nullable AlbumListData result) { if (result == null) { pasta.onError(PlayerActivity.this, "album menu action"); return; } Intent i = new Intent(PlayerActivity.this, HomeActivity.class); i.putExtra("album", result); startActivity(i); } }.execute(); break; case R.id.action_artist: TrackListData track = trackList.get(curPosition); if (track.artists.size() > 0) { new Action<ArtistListData>() { @NonNull @Override public String id() { return "gotoArtist"; } @Nullable @Override protected ArtistListData run() throws InterruptedException { if (trackList != null) return pasta.getArtist(trackList.get(curPosition).artists.get(0).artistId); else return null; } @Override protected void done(@Nullable ArtistListData result) { if (result == null) { pasta.onError(PlayerActivity.this, "artist menu action"); return; } Intent i = new Intent(PlayerActivity.this, HomeActivity.class); i.putExtra("artist", result); startActivity(i); } }.execute(); } break; } return super.onOptionsItemSelected(item); } @Override public void onConfigurationChanged(Configuration newConfig) { super.onConfigurationChanged(newConfig); //orientation changed, layout is invalid finish(); } @Override public void finish() { super.finish(); overridePendingTransition(0, R.anim.slide_down); } private void setClickable(boolean clickable) { playButton.setClickable(clickable); prevButton.setClickable(clickable); nextButton.setClickable(clickable); seekBar.setClickable(clickable); } private boolean isClickable() { return playButton.isClickable(); } private void setLoading(boolean loading) { progressBar.setVisibility(loading ? View.VISIBLE : View.GONE); } private boolean isLoading() { return progressBar.getVisibility() == View.VISIBLE; } private class UpdateReceiver extends BroadcastReceiver { @Override public void onReceive(Context context, final Intent intent) { playing = intent.getBooleanExtra(PlayerService.EXTRA_PLAYING, false); curPosition = intent.getIntExtra(PlayerService.EXTRA_CUR_POSITION, -1); int curTime = intent.getIntExtra(PlayerService.EXTRA_CUR_TIME, -1); int maxTime = intent.getIntExtra(PlayerService.EXTRA_MAX_TIME, -1); data = intent.getParcelableExtra(PlayerService.EXTRA_CUR_TRACK); ArrayList<TrackListData> list = intent.getParcelableArrayListExtra(PlayerService.EXTRA_TRACK_LIST); if (trackList == null || !trackList.equals(list)) trackList = list; if ((lastUri == null || !data.trackUri.matches(lastUri))) { if (!isLoading()) setLoading(true); if (action != null && action.isExecuting()) action.cancel(); Glide.with(PlayerActivity.this).load(data.trackImageLarge).placeholder(ImageUtils.getVectorDrawable(PlayerActivity.this, R.drawable.preload)).into(new GlideDrawableImageViewTarget(art) { @Override public void onResourceReady(GlideDrawable resource, GlideAnimation<? super GlideDrawable> animation) { art.transition(resource); if (isLoading()) setLoading(false); Bitmap bitmap = ImageUtils.drawableToBitmap(resource); if (backgroundImage != null) backgroundImage.transition(ImageUtils.blurBitmap(bitmap)); if (palette) { Palette.from(bitmap).generate(new Palette.PaletteAsyncListener() { @Override public void onGenerated(Palette palette) { final int color; if (PreferenceUtils.isDarkTheme(PlayerActivity.this)) color = palette.getDarkVibrantColor(Color.DKGRAY); else color = palette.getLightVibrantColor(Color.LTGRAY); ValueAnimator animator = ValueAnimator.ofInt(-100, 100); animator.setDuration(250); animator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() { boolean set; @Override public void onAnimationUpdate(ValueAnimator animation) { int value = (int) animation.getAnimatedValue(); if (value >= 0 && !set) { bg.setBackgroundColor(color); set = true; } bg.getBackground().setAlpha(Math.abs(value)); } }); animator.start(); if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) setTaskDescription(new ActivityManager.TaskDescription(data.trackName, ImageUtils.drawableToBitmap(ContextCompat.getDrawable(PlayerActivity.this, R.mipmap.ic_launcher)), color)); } }); } } @Override public void onLoadFailed(Exception e, Drawable errorDrawable) { super.onLoadFailed(e, errorDrawable); if (backgroundImage != null) backgroundImage.transition(ContextCompat.getDrawable(PlayerActivity.this, R.drawable.image_gradient)); if (isLoading()) setLoading(false); } }); action = new Action<Boolean>() { @NonNull @Override public String id() { return "isTrackFav"; } @Nullable @Override protected Boolean run() throws InterruptedException { return pasta.isFavorite(trackList.get(curPosition)); } @Override protected void done(@Nullable Boolean result) { if (result == null) { pasta.onError(PlayerActivity.this, "favorite action"); return; } if (result) { fav.setIcon(R.drawable.ic_fav); } else { fav.setIcon(R.drawable.ic_unfav); } } }; title.setText(data.trackName); if (data.artists.size() > 0) subtitle.setText(data.artists.get(0).artistName); else subtitle.setText(""); if (subtitle2 != null) subtitle2.setText(data.albumName); adapter.swapData(trackList, curPosition); lastUri = data.trackUri; } playButton.setImageDrawable(playing ? pause : play); if (!isClickable()) setClickable(true); if (seekBar.getProgress() != curTime && !playbarDragging){ ObjectAnimator.ofInt(seekBar, "progress", curTime).setDuration(PlayerService.UPDATE_INTERVAL).start(); String current = String.valueOf(curTime); currentDuration.setText(StaticUtils.timeToString((Integer.parseInt(current) / 1000) / 60, (Integer.parseInt(current) / 1000) % 60)); } if (seekBar.getMax() != maxTime) { seekBar.setMax(maxTime); String duration = String.valueOf(maxTime); finalDuration.setText(StaticUtils.timeToString((Integer.parseInt(duration) / 1000) / 60, (Integer.parseInt(duration) / 1000) % 60)); } } } }