diff --git a/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java index b9415167..c6f38929 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java +++ b/app/src/main/java/com/kabouzeid/gramophone/adapter/AlbumCoverPagerAdapter.java @@ -1,27 +1,22 @@ package com.kabouzeid.gramophone.adapter; -import android.animation.Animator; import android.content.SharedPreferences; import android.graphics.Bitmap; import android.os.Bundle; import android.support.annotation.Nullable; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; -import android.view.animation.AccelerateInterpolator; -import android.view.animation.DecelerateInterpolator; import android.widget.ImageView; import com.kabouzeid.gramophone.R; -import com.kabouzeid.gramophone.misc.SimpleAnimatorListener; +import com.kabouzeid.gramophone.misc.CustomFragmentStatePagerAdapter; import com.kabouzeid.gramophone.model.Song; import com.kabouzeid.gramophone.util.ColorUtil; import com.kabouzeid.gramophone.util.MusicUtil; import com.kabouzeid.gramophone.util.PreferenceUtil; -import com.kabouzeid.gramophone.util.ViewUtil; import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.assist.FailReason; @@ -36,7 +31,7 @@ import butterknife.ButterKnife; /** * @author Karim Abou Zeid (kabouzeid) */ -public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { +public class AlbumCoverPagerAdapter extends CustomFragmentStatePagerAdapter { public static final String TAG = AlbumCoverPagerAdapter.class.getSimpleName(); private ArrayList dataSet; @@ -61,11 +56,12 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { @Bind(R.id.player_image) ImageView albumCover; - @Bind(R.id.player_favorite_icon) - ImageView favoriteIcon; + private boolean isColorReady; private int color; private Song song; + private ColorReceiver colorReceiver; + private int request; public static AlbumCoverFragment newInstance(final Song song) { AlbumCoverFragment frag = new AlbumCoverFragment(); @@ -102,6 +98,7 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { super.onDestroyView(); PreferenceUtil.getInstance(getActivity()).unregisterOnSharedPreferenceChangedListener(this); ButterKnife.unbind(this); + colorReceiver = null; } private void loadAlbumCover() { @@ -114,6 +111,7 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { .postProcessor(new BitmapProcessor() { @Override public Bitmap process(Bitmap bitmap) { + // don't use set color here, as this is not running on the ui-thread color = ColorUtil.generateColor(getActivity(), bitmap); return bitmap; } @@ -122,8 +120,7 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { new SimpleImageLoadingListener() { @Override public void onLoadingFailed(String imageUri, View view, @Nullable FailReason failReason) { - color = ColorUtil.resolveColor(getActivity(), R.attr.default_bar_color); - notifyColorIsReady(); + setColor(ColorUtil.resolveColor(view.getContext(), R.attr.default_bar_color)); } @Override @@ -132,49 +129,12 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { onLoadingFailed(imageUri, view, null); return; } - notifyColorIsReady(); + setColor(color); } } ); } - public void showHeartAnimation() { - favoriteIcon.clearAnimation(); - - favoriteIcon.setAlpha(0f); - favoriteIcon.setScaleX(0f); - favoriteIcon.setScaleY(0f); - favoriteIcon.setVisibility(View.VISIBLE); - favoriteIcon.setPivotX(favoriteIcon.getWidth() / 2); - favoriteIcon.setPivotY(favoriteIcon.getHeight() / 2); - - favoriteIcon.animate() - .setDuration(ViewUtil.PHONOGRAPH_ANIM_TIME / 2) - .setInterpolator(new DecelerateInterpolator()) - .scaleX(1f) - .scaleY(1f) - .alpha(1f) - .setListener(new SimpleAnimatorListener() { - @Override - public void onAnimationCancel(Animator animation) { - favoriteIcon.setVisibility(View.INVISIBLE); - } - }) - .withEndAction(new Runnable() { - @Override - public void run() { - favoriteIcon.animate() - .setDuration(ViewUtil.PHONOGRAPH_ANIM_TIME / 2) - .setInterpolator(new AccelerateInterpolator()) - .scaleX(0f) - .scaleY(0f) - .alpha(0f) - .start(); - } - }) - .start(); - } - @Override public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) { switch (key) { @@ -188,8 +148,26 @@ public class AlbumCoverPagerAdapter extends FragmentStatePagerAdapter { albumCover.setScaleType(forceSquareAlbumCover ? ImageView.ScaleType.FIT_CENTER : ImageView.ScaleType.CENTER_CROP); } - private void notifyColorIsReady() { - // TODO + private void setColor(int color) { + this.color = color; + isColorReady = true; + if (colorReceiver != null) { + colorReceiver.onColorReady(color, request); + colorReceiver = null; + } + } + + public void receiveColor(ColorReceiver colorReceiver, int request) { + if (isColorReady) { + colorReceiver.onColorReady(color, request); + } else { + this.colorReceiver = colorReceiver; + this.request = request; + } + } + + public interface ColorReceiver { + void onColorReady(int color, int request); } } } diff --git a/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java b/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java new file mode 100644 index 00000000..378a67f7 --- /dev/null +++ b/app/src/main/java/com/kabouzeid/gramophone/misc/CustomFragmentStatePagerAdapter.java @@ -0,0 +1,235 @@ +package com.kabouzeid.gramophone.misc; + +/* + * Copyright (C) 2011 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +import android.os.Bundle; +import android.os.Parcelable; +import android.support.v4.app.Fragment; +import android.support.v4.app.FragmentManager; +import android.support.v4.app.FragmentPagerAdapter; +import android.support.v4.app.FragmentTransaction; +import android.util.Log; +import android.view.View; +import android.view.ViewGroup; + +import java.util.ArrayList; + +/** + * Implementation of {@link android.support.v4.view.PagerAdapter} that + * uses a {@link Fragment} to manage each page. This class also handles + * saving and restoring of fragment's state. + *

+ *

This version of the pager is more useful when there are a large number + * of pages, working more like a list view. When pages are not visible to + * the user, their entire fragment may be destroyed, only keeping the saved + * state of that fragment. This allows the pager to hold on to much less + * memory associated with each visited page as compared to + * {@link FragmentPagerAdapter} at the cost of potentially more overhead when + * switching between pages. + *

+ *

When using FragmentPagerAdapter the host ViewPager must have a + * valid ID set.

+ *

+ *

Subclasses only need to implement {@link #getItem(int)} + * and {@link #getCount()} to have a working adapter. + *

+ *

Here is an example implementation of a pager containing fragments of + * lists: + *

+ * {@sample development/samples/Support13Demos/src/com/example/android/supportv13/app/FragmentStatePagerSupport.java + * complete} + *

+ *

The R.layout.fragment_pager resource of the top-level fragment is: + *

+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager.xml + * complete} + *

+ *

The R.layout.fragment_pager_list resource containing each + * individual fragment's layout is: + *

+ * {@sample development/samples/Support13Demos/res/layout/fragment_pager_list.xml + * complete} + */ +public abstract class CustomFragmentStatePagerAdapter extends android.support.v4.view.PagerAdapter { + public static final String TAG = CustomFragmentStatePagerAdapter.class.getSimpleName(); + private static final boolean DEBUG = false; + + private final FragmentManager mFragmentManager; + private FragmentTransaction mCurTransaction = null; + + private ArrayList mSavedState = new ArrayList(); + private ArrayList mFragments = new ArrayList(); + private Fragment mCurrentPrimaryItem = null; + + public CustomFragmentStatePagerAdapter(FragmentManager fm) { + mFragmentManager = fm; + } + + /** + * Return the Fragment associated with a specified position. + */ + public abstract Fragment getItem(int position); + + @Override + public void startUpdate(ViewGroup container) { + } + + @Override + public Object instantiateItem(ViewGroup container, int position) { + // If we already have this item instantiated, there is nothing + // to do. This can happen when we are restoring the entire pager + // from its saved state, where the fragment manager has already + // taken care of restoring the fragments we previously had instantiated. + if (mFragments.size() > position) { + Fragment f = mFragments.get(position); + if (f != null) { + return f; + } + } + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + + Fragment fragment = getItem(position); + if (DEBUG) Log.v(TAG, "Adding item #" + position + ": f=" + fragment); + if (mSavedState.size() > position) { + Fragment.SavedState fss = mSavedState.get(position); + if (fss != null) { + fragment.setInitialSavedState(fss); + } + } + while (mFragments.size() <= position) { + mFragments.add(null); + } + fragment.setMenuVisibility(false); + fragment.setUserVisibleHint(false); + mFragments.set(position, fragment); + mCurTransaction.add(container.getId(), fragment); + + return fragment; + } + + @Override + public void destroyItem(ViewGroup container, int position, Object object) { + Fragment fragment = (Fragment) object; + + if (mCurTransaction == null) { + mCurTransaction = mFragmentManager.beginTransaction(); + } + if (DEBUG) Log.v(TAG, "Removing item #" + position + ": f=" + object + + " v=" + ((Fragment) object).getView()); + while (mSavedState.size() <= position) { + mSavedState.add(null); + } + mSavedState.set(position, mFragmentManager.saveFragmentInstanceState(fragment)); + mFragments.set(position, null); + + mCurTransaction.remove(fragment); + } + + @Override + public void setPrimaryItem(ViewGroup container, int position, Object object) { + Fragment fragment = (Fragment) object; + if (fragment != mCurrentPrimaryItem) { + if (mCurrentPrimaryItem != null) { + mCurrentPrimaryItem.setMenuVisibility(false); + mCurrentPrimaryItem.setUserVisibleHint(false); + } + if (fragment != null) { + fragment.setMenuVisibility(true); + fragment.setUserVisibleHint(true); + } + mCurrentPrimaryItem = fragment; + } + } + + @Override + public void finishUpdate(ViewGroup container) { + if (mCurTransaction != null) { + mCurTransaction.commitAllowingStateLoss(); + mCurTransaction = null; + mFragmentManager.executePendingTransactions(); + } + } + + @Override + public boolean isViewFromObject(View view, Object object) { + return ((Fragment) object).getView() == view; + } + + @Override + public Parcelable saveState() { + Bundle state = null; + if (mSavedState.size() > 0) { + state = new Bundle(); + Fragment.SavedState[] fss = new Fragment.SavedState[mSavedState.size()]; + mSavedState.toArray(fss); + state.putParcelableArray("states", fss); + } + for (int i = 0; i < mFragments.size(); i++) { + Fragment f = mFragments.get(i); + if (f != null && f.isAdded()) { + if (state == null) { + state = new Bundle(); + } + String key = "f" + i; + mFragmentManager.putFragment(state, key, f); + } + } + return state; + } + + @Override + public void restoreState(Parcelable state, ClassLoader loader) { + if (state != null) { + Bundle bundle = (Bundle) state; + bundle.setClassLoader(loader); + Parcelable[] fss = bundle.getParcelableArray("states"); + mSavedState.clear(); + mFragments.clear(); + if (fss != null) { + for (int i = 0; i < fss.length; i++) { + mSavedState.add((Fragment.SavedState) fss[i]); + } + } + Iterable keys = bundle.keySet(); + for (String key : keys) { + if (key.startsWith("f")) { + int index = Integer.parseInt(key.substring(1)); + Fragment f = mFragmentManager.getFragment(bundle, key); + if (f != null) { + while (mFragments.size() <= index) { + mFragments.add(null); + } + f.setMenuVisibility(false); + mFragments.set(index, f); + } else { + Log.w(TAG, "Bad fragment at key " + key); + } + } + } + } + } + + public Fragment getFragment(int position) { + if (position < mFragments.size() && position >= 0) { + return mFragments.get(position); + } + return null; + } +} diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java index 20cd91f0..4bf4f96c 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/activities/base/AbsSlidingMusicPanelActivity.java @@ -57,7 +57,7 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi } slidingUpPanelLayout.setPanelSlideListener(this); - playerFragment.onShow(); + playerFragment.onHide(); slidingUpPanelLayout.post(new Runnable() { @Override @@ -65,6 +65,8 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi if (!isPanelCollapsed()) { onPanelSlide(slidingUpPanelLayout, 1); onPanelExpanded(slidingUpPanelLayout); + } else if (isPanelCollapsed()) { + onPanelCollapsed(slidingUpPanelLayout); } } }); @@ -99,7 +101,9 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi if (shouldColorNavigationBar()) { super.setNavigationBarColor(navigationBarColor); } - playerFragment.onShow(); + playerFragment.setMenuVisibility(false); + playerFragment.setUserVisibleHint(false); + playerFragment.onHide(); } @Override @@ -109,7 +113,9 @@ public abstract class AbsSlidingMusicPanelActivity extends AbsMusicServiceActivi if (shouldColorNavigationBar()) { super.setNavigationBarColor(playerFragmentColor); } - playerFragment.onHide(); + playerFragment.setMenuVisibility(true); + playerFragment.setUserVisibleHint(true); + playerFragment.onShow(); } @Override diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java index 5f1a8771..33ca3419 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/AbsPlayerFragment.java @@ -83,10 +83,10 @@ public abstract class AbsPlayerFragment extends AbsMusicServiceFragment implemen return false; } - public abstract void onHide(); - public abstract void onShow(); + public abstract void onHide(); + public abstract boolean onBackPressed(); public Callbacks getCallbacks() { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlaybackControlsFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlaybackControlsFragment.java index 5c00162c..984e2ef9 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlaybackControlsFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlaybackControlsFragment.java @@ -267,7 +267,7 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv } } - public void hide() { + public void show() { playPauseFab.animate() .scaleX(1f) .scaleY(1f) @@ -276,7 +276,7 @@ public class PlaybackControlsFragment extends Fragment implements MusicServiceEv .start(); } - public void show() { + public void hide() { playPauseFab.setScaleX(0f); playPauseFab.setScaleY(0f); playPauseFab.setRotation(0f); diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java index aed46c70..c28eabd1 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerAlbumCoverFragment.java @@ -1,16 +1,23 @@ package com.kabouzeid.gramophone.ui.fragments.player; +import android.animation.Animator; import android.os.Bundle; import android.support.annotation.ColorInt; import android.support.v4.view.ViewPager; +import android.util.Log; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; +import android.view.animation.AccelerateInterpolator; +import android.view.animation.DecelerateInterpolator; +import android.widget.ImageView; import com.kabouzeid.gramophone.R; import com.kabouzeid.gramophone.adapter.AlbumCoverPagerAdapter; import com.kabouzeid.gramophone.helper.MusicPlayerRemote; +import com.kabouzeid.gramophone.misc.SimpleAnimatorListener; import com.kabouzeid.gramophone.ui.fragments.AbsMusicServiceFragment; +import com.kabouzeid.gramophone.util.ViewUtil; import butterknife.Bind; import butterknife.ButterKnife; @@ -19,11 +26,15 @@ import butterknife.ButterKnife; * @author Karim Abou Zeid (kabouzeid) */ public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements ViewPager.OnPageChangeListener { + public static final String TAG = PlayerAlbumCoverFragment.class.getSimpleName(); @Bind(R.id.player_album_cover_viewpager) ViewPager viewPager; + @Bind(R.id.player_favorite_icon) + ImageView favoriteIcon; private OnColorChangedListener onColorChangedListener; + private int currentRequest; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, @@ -70,11 +81,26 @@ public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements @Override public void onPageSelected(int position) { + currentRequest = position; + AlbumCoverPagerAdapter.AlbumCoverFragment albumCoverFragment = ((AlbumCoverPagerAdapter.AlbumCoverFragment) ((AlbumCoverPagerAdapter) viewPager.getAdapter()).getFragment(position)); + if (albumCoverFragment != null) { + albumCoverFragment.receiveColor(colorReceiver, position); + } if (position != MusicPlayerRemote.getPosition()) { MusicPlayerRemote.playSongAt(position); } } + private AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver colorReceiver = new AlbumCoverPagerAdapter.AlbumCoverFragment.ColorReceiver() { + @Override + public void onColorReady(int color, int request) { + Log.d(TAG, "currentRequest == request : " + (currentRequest == request)); + if (currentRequest == request) { + notifyColorChange(color); + } + } + }; + @Override public void onPageScrollStateChanged(int state) { @@ -86,7 +112,44 @@ public class PlayerAlbumCoverFragment extends AbsMusicServiceFragment implements } public void showHeartAnimation() { + favoriteIcon.clearAnimation(); + favoriteIcon.setAlpha(0f); + favoriteIcon.setScaleX(0f); + favoriteIcon.setScaleY(0f); + favoriteIcon.setVisibility(View.VISIBLE); + favoriteIcon.setPivotX(favoriteIcon.getWidth() / 2); + favoriteIcon.setPivotY(favoriteIcon.getHeight() / 2); + + favoriteIcon.animate() + .setDuration(ViewUtil.PHONOGRAPH_ANIM_TIME / 2) + .setInterpolator(new DecelerateInterpolator()) + .scaleX(1f) + .scaleY(1f) + .alpha(1f) + .setListener(new SimpleAnimatorListener() { + @Override + public void onAnimationCancel(Animator animation) { + favoriteIcon.setVisibility(View.INVISIBLE); + } + }) + .withEndAction(new Runnable() { + @Override + public void run() { + favoriteIcon.animate() + .setDuration(ViewUtil.PHONOGRAPH_ANIM_TIME / 2) + .setInterpolator(new AccelerateInterpolator()) + .scaleX(0f) + .scaleY(0f) + .alpha(0f) + .start(); + } + }) + .start(); + } + + private void notifyColorChange(int color) { + if (onColorChangedListener != null) onColorChangedListener.onColorChanged(color); } public void setOnColorChangedListener(OnColorChangedListener listener) { diff --git a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerFragment.java b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerFragment.java index 707049c5..b5a20d91 100644 --- a/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerFragment.java +++ b/app/src/main/java/com/kabouzeid/gramophone/ui/fragments/player/PlayerFragment.java @@ -111,6 +111,8 @@ public class PlayerFragment extends AbsPlayerFragment implements PlayerAlbumCove } }); + playingQueueCard.setCardBackgroundColor(ColorUtil.resolveColor(getActivity(), R.attr.cardBackgroundColor)); + setUpCurrentSongView(); } @@ -267,13 +269,13 @@ public class PlayerFragment extends AbsPlayerFragment implements PlayerAlbumCove } @Override - public void onHide() { - playbackControlsFragment.hide(); + public void onShow() { + playbackControlsFragment.show(); } @Override - public void onShow() { - playbackControlsFragment.show(); + public void onHide() { + playbackControlsFragment.hide(); } @Override diff --git a/app/src/main/res/layout/fragment_album_cover.xml b/app/src/main/res/layout/fragment_album_cover.xml index 12055764..e05aebd5 100644 --- a/app/src/main/res/layout/fragment_album_cover.xml +++ b/app/src/main/res/layout/fragment_album_cover.xml @@ -13,14 +13,4 @@ android:scaleType="centerCrop" tools:ignore="ContentDescription,UnusedAttribute" /> - - \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_player_album_cover.xml b/app/src/main/res/layout/fragment_player_album_cover.xml index aa15aa3b..9a6a7afe 100644 --- a/app/src/main/res/layout/fragment_player_album_cover.xml +++ b/app/src/main/res/layout/fragment_player_album_cover.xml @@ -1,5 +1,6 @@ @@ -7,4 +8,14 @@ android:id="@+id/player_album_cover_viewpager" android:layout_width="match_parent" android:layout_height="match_parent" /> + + \ No newline at end of file