Merged fastscroller from cabinet. Temporary fixed the wrong fab margins on KitKat and below caused by the google support design library. Added empty screens to Album Song and Artist views.

This commit is contained in:
Karim Abou Zeid 2015-06-02 21:27:02 +02:00
commit 3082ed1187
30 changed files with 483 additions and 195 deletions

View file

@ -27,14 +27,10 @@ android {
applicationId "com.kabouzeid.gramophone"
minSdkVersion 16
targetSdkVersion 22
versionCode 29
versionName "0.9.13b DEV-3"
versionCode 30
versionName "0.9.14b DEV"
}
compileOptions {
sourceCompatibility JavaVersion.VERSION_1_7
targetCompatibility JavaVersion.VERSION_1_7
}
buildTypes {
release {
minifyEnabled true

View file

@ -23,6 +23,8 @@ import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.OnUpdatedListener;
import com.kabouzeid.gramophone.interfaces.SelfUpdating;
import com.kabouzeid.gramophone.loader.AlbumLoader;
import com.kabouzeid.gramophone.loader.AlbumSongLoader;
import com.kabouzeid.gramophone.model.Album;
@ -47,12 +49,13 @@ import java.util.List;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album> {
public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder, Album> implements SelfUpdating {
public static final String TAG = AlbumAdapter.class.getSimpleName();
private final AppCompatActivity activity;
private boolean usePalette;
private List<Album> dataSet;
private OnUpdatedListener listener;
@Override
public ViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
@ -130,6 +133,11 @@ public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder,
return songs;
}
@Override
public void setOnUpdatedListener(OnUpdatedListener listener) {
this.listener = listener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final ImageView albumArt;
final TextView title;
@ -193,6 +201,7 @@ public class AlbumAdapter extends AbsMultiSelectAdapter<AlbumAdapter.ViewHolder,
private void loadDataSet() {
dataSet = AlbumLoader.getAllAlbums(activity);
if (listener != null) listener.onUpdated(this);
}
private void applyPalette(Bitmap bitmap, final TextView title, final TextView artist, final View footer) {

View file

@ -17,6 +17,8 @@ import com.kabouzeid.gramophone.dialogs.AddToPlaylistDialog;
import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.OnUpdatedListener;
import com.kabouzeid.gramophone.interfaces.SelfUpdating;
import com.kabouzeid.gramophone.lastfm.artist.LastFMArtistThumbnailUrlLoader;
import com.kabouzeid.gramophone.loader.ArtistLoader;
import com.kabouzeid.gramophone.loader.ArtistSongLoader;
@ -36,9 +38,10 @@ import java.util.List;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist> {
public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolder, Artist> implements SelfUpdating {
protected final AppCompatActivity activity;
protected List<Artist> dataSet;
private OnUpdatedListener listener;
public ArtistAdapter(AppCompatActivity activity, @Nullable CabHolder cabHolder) {
super(cabHolder, R.menu.menu_media_selection);
@ -48,6 +51,7 @@ public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolde
private void loadDataSet() {
dataSet = ArtistLoader.getAllArtists(activity);
if (listener != null) listener.onUpdated(this);
}
@Override
@ -112,6 +116,11 @@ public class ArtistAdapter extends AbsMultiSelectAdapter<ArtistAdapter.ViewHolde
return songs;
}
@Override
public void setOnUpdatedListener(OnUpdatedListener listener) {
this.listener = listener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final TextView artistName;
final TextView artistInfo;

View file

@ -18,6 +18,8 @@ import com.kabouzeid.gramophone.dialogs.DeletePlaylistDialog;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.OnUpdatedListener;
import com.kabouzeid.gramophone.interfaces.SelfUpdating;
import com.kabouzeid.gramophone.loader.PlaylistLoader;
import com.kabouzeid.gramophone.loader.PlaylistSongLoader;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
@ -33,11 +35,12 @@ import java.util.List;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, Playlist> {
public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewHolder, Playlist> implements SelfUpdating {
public static final String TAG = PlaylistAdapter.class.getSimpleName();
protected final AppCompatActivity activity;
protected List<Playlist> dataSet;
private OnUpdatedListener listener;
public PlaylistAdapter(AppCompatActivity activity, @Nullable CabHolder cabHolder) {
super(cabHolder, R.menu.menu_playlists_selection);
@ -47,6 +50,7 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewH
public void loadDataSet() {
dataSet = PlaylistLoader.getAllPlaylists(activity);
if (listener != null) listener.onUpdated(this);
}
@Override
@ -95,6 +99,11 @@ public class PlaylistAdapter extends AbsMultiSelectAdapter<PlaylistAdapter.ViewH
return songs;
}
@Override
public void setOnUpdatedListener(OnUpdatedListener listener) {
this.listener = listener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
public final TextView playlistName;
private final View menu;

View file

@ -22,6 +22,8 @@ import com.kabouzeid.gramophone.dialogs.DeleteSongsDialog;
import com.kabouzeid.gramophone.helper.MenuItemClickHelper;
import com.kabouzeid.gramophone.helper.MusicPlayerRemote;
import com.kabouzeid.gramophone.interfaces.CabHolder;
import com.kabouzeid.gramophone.interfaces.OnUpdatedListener;
import com.kabouzeid.gramophone.interfaces.SelfUpdating;
import com.kabouzeid.gramophone.loader.SongLoader;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
import com.kabouzeid.gramophone.model.Song;
@ -37,7 +39,7 @@ import java.util.ArrayList;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, Song> implements MaterialCab.Callback {
public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, Song> implements MaterialCab.Callback, SelfUpdating {
public static final String TAG = AlbumSongAdapter.class.getSimpleName();
private static final int SHUFFLE_BUTTON = 0;
@ -45,6 +47,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
protected final AppCompatActivity activity;
protected ArrayList<Song> dataSet;
private OnUpdatedListener listener;
public SongAdapter(AppCompatActivity activity, CabHolder cabHolder) {
super(cabHolder, R.menu.menu_media_selection);
@ -54,6 +57,7 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
private void loadDataSet() {
dataSet = SongLoader.getAllSongs(activity);
if (listener != null) listener.onUpdated(this);
}
@Override
@ -101,7 +105,8 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
@Override
public int getItemCount() {
return dataSet.size() + 1;
final int dataSetSize = dataSet.size();
return dataSetSize > 0 ? dataSetSize + 1 : 0;
}
@Override
@ -124,6 +129,11 @@ public class SongAdapter extends AbsMultiSelectAdapter<SongAdapter.ViewHolder, S
}
}
@Override
public void setOnUpdatedListener(OnUpdatedListener listener) {
this.listener = listener;
}
public class ViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener, View.OnLongClickListener {
final TextView songTitle;
final TextView songInfo;

View file

@ -0,0 +1,9 @@
package com.kabouzeid.gramophone.interfaces;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public interface OnUpdatedListener {
void onUpdated(SelfUpdating selfUpdating);
}

View file

@ -0,0 +1,9 @@
package com.kabouzeid.gramophone.interfaces;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public interface SelfUpdating {
void setOnUpdatedListener(OnUpdatedListener listener);
}

View file

@ -3,7 +3,6 @@ package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.interfaces.KabViewsDisableAble;
import com.kabouzeid.gramophone.ui.activities.MainActivity;
@ -13,14 +12,6 @@ import com.kabouzeid.gramophone.ui.activities.MainActivity;
public abstract class AbsMainActivityFragment extends Fragment implements KabViewsDisableAble {
private boolean areViewsEnabled;
protected int getTopPadding() {
return getResources().getDimensionPixelSize(R.dimen.list_padding_vertical);
}
protected int getBottomPadding() {
return getResources().getDimensionPixelSize(R.dimen.bottom_offset_fab_activity);
}
protected MainActivity getMainActivity() {
return (MainActivity) getActivity();
}

View file

@ -1,17 +1,23 @@
package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments;
import android.os.Bundle;
import android.support.annotation.StringRes;
import android.support.v7.widget.RecyclerView;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.interfaces.OnUpdatedListener;
import com.kabouzeid.gramophone.interfaces.SelfUpdating;
import com.kabouzeid.gramophone.views.FastScroller;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivityFragment {
public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivityFragment implements OnUpdatedListener {
public static final String TAG = AbsMainActivityRecyclerViewFragment.class.getSimpleName();
private RecyclerView recyclerView;
@ -19,20 +25,35 @@ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivit
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(getLayoutResId(), container, false);
return inflater.inflate(R.layout.fragment_main_activity_recycler_view, container, false);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
recyclerView = (RecyclerView) view.findViewById(R.id.recycler_view);
final FastScroller fastScroller = (FastScroller) view.findViewById(R.id.fast_scroller);
fastScroller.setRecyclerView(recyclerView);
fastScroller.setOnHandleTouchListener(new View.OnTouchListener() {
@Override
public boolean onTouch(View v, MotionEvent event) {
return false;
}
});
fastScroller.setPressedHandleColor(getMainActivity().getThemeColorPrimary());
setUpRecyclerView();
checkAndProcessAdapterSize();
}
private void setUpRecyclerView() {
recyclerView.setLayoutManager(createLayoutManager());
recyclerView.setPadding(0, getTopPadding(), 0, getBottomPadding());
mAdapter = createAdapter();
if (mAdapter instanceof SelfUpdating) ((SelfUpdating) mAdapter).setOnUpdatedListener(this);
recyclerView.setLayoutManager(createLayoutManager());
recyclerView.setAdapter(mAdapter);
}
@ -52,9 +73,28 @@ public abstract class AbsMainActivityRecyclerViewFragment extends AbsMainActivit
recyclerView.setEnabled(false);
}
protected abstract int getLayoutResId();
private void checkAndProcessAdapterSize() {
final View v = getView();
RecyclerView.Adapter adapter = getAdapter();
if (adapter != null && v != null) {
final TextView emptyTextView = (TextView) v.findViewById(android.R.id.empty);
emptyTextView.setText(getEmptyMessage());
emptyTextView.setVisibility(adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
}
@StringRes
protected int getEmptyMessage() {
return R.string.nothing_here;
}
protected abstract RecyclerView.LayoutManager createLayoutManager();
protected abstract RecyclerView.Adapter createAdapter();
@Override
public void onUpdated(SelfUpdating selfUpdating) {
checkAndProcessAdapterSize();
}
}

View file

@ -16,11 +16,6 @@ public class AlbumViewFragment extends AbsMainActivityRecyclerViewFragment {
private GridLayoutManager layoutManager;
@Override
protected int getLayoutResId() {
return R.layout.fragment_album_view;
}
@Override
protected RecyclerView.LayoutManager createLayoutManager() {
int columns = Util.isInPortraitMode(getActivity()) ? PreferenceUtils.getInstance(getActivity()).getAlbumGridColumns() : PreferenceUtils.getInstance(getActivity()).getAlbumGridColumnsLand();
@ -33,6 +28,11 @@ public class AlbumViewFragment extends AbsMainActivityRecyclerViewFragment {
return new AlbumAdapter(getMainActivity(), getMainActivity());
}
@Override
protected int getEmptyMessage() {
return R.string.no_albums;
}
public void setColumns(int columns) {
layoutManager.setSpanCount(columns);
layoutManager.requestLayout();

View file

@ -1,19 +1,17 @@
package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.ArtistAdapter;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class ArtistViewFragment extends AbsMainActivityRecyclerViewFragment {
public static final String TAG = ArtistViewFragment.class.getSimpleName();
@Override
protected int getLayoutResId() {
return R.layout.fragment_artist_view;
}
public static final String TAG = ArtistViewFragment.class.getSimpleName();
@Override
protected RecyclerView.LayoutManager createLayoutManager() {
@ -24,4 +22,9 @@ public class ArtistViewFragment extends AbsMainActivityRecyclerViewFragment {
protected RecyclerView.Adapter createAdapter() {
return new ArtistAdapter(getMainActivity(), getMainActivity());
}
@Override
protected int getEmptyMessage() {
return R.string.no_artists;
}
}

View file

@ -1,68 +1,30 @@
package com.kabouzeid.gramophone.ui.fragments.mainactivityfragments;
import android.os.Bundle;
import android.support.v7.widget.GridLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import com.kabouzeid.gramophone.App;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.adapter.PlaylistAdapter;
import com.kabouzeid.gramophone.model.DataBaseChangedEvent;
import com.squareup.otto.Subscribe;
/**
* @author Karim Abou Zeid (kabouzeid)
*/
public class PlaylistViewFragment extends AbsMainActivityRecyclerViewFragment {
public static final String TAG = PlaylistViewFragment.class.getSimpleName();
@Override
protected int getLayoutResId() {
return R.layout.fragment_playlist_view;
}
@Override
protected RecyclerView.LayoutManager createLayoutManager() {
return new GridLayoutManager(getActivity(), 1);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
App.bus.register(this);
}
@Override
public void onDestroy() {
super.onDestroy();
App.bus.unregister(this);
}
@Override
protected RecyclerView.Adapter createAdapter() {
PlaylistAdapter adapter = new PlaylistAdapter(getMainActivity(), getMainActivity());
View v = getView();
if (v != null) {
v.findViewById(android.R.id.empty).setVisibility(
adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
return adapter;
return new PlaylistAdapter(getMainActivity(), getMainActivity());
}
@Subscribe
public void onDataBaseEvent(DataBaseChangedEvent event) {
switch (event.getAction()) {
case DataBaseChangedEvent.PLAYLISTS_CHANGED:
case DataBaseChangedEvent.DATABASE_CHANGED:
PlaylistAdapter adapter = (PlaylistAdapter) getAdapter();
adapter.loadDataSet();
adapter.notifyDataSetChanged();
View v = getView();
if (v != null) {
v.findViewById(android.R.id.empty).setVisibility(
adapter.getItemCount() == 0 ? View.VISIBLE : View.GONE);
}
break;
}
@Override
protected int getEmptyMessage() {
return R.string.no_playlists;
}
}

View file

@ -13,11 +13,6 @@ public class SongViewFragment extends AbsMainActivityRecyclerViewFragment {
public static final String TAG = SongViewFragment.class.getSimpleName();
@Override
protected int getLayoutResId() {
return R.layout.fragment_songview;
}
@Override
protected RecyclerView.LayoutManager createLayoutManager() {
return new GridLayoutManager(getActivity(), 1);
@ -27,4 +22,9 @@ public class SongViewFragment extends AbsMainActivityRecyclerViewFragment {
protected RecyclerView.Adapter createAdapter() {
return new SongAdapter(getMainActivity(), getMainActivity());
}
@Override
protected int getEmptyMessage() {
return R.string.no_songs;
}
}

View file

@ -238,4 +238,12 @@ public class Util {
new int[]{color}
);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public static boolean isRTL(Context context) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
Configuration config = context.getResources().getConfiguration();
return config.getLayoutDirection() == View.LAYOUT_DIRECTION_RTL;
} else return false;
}
}

View file

@ -0,0 +1,248 @@
package com.kabouzeid.gramophone.views;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.InsetDrawable;
import android.graphics.drawable.StateListDrawable;
import android.support.v4.view.animation.FastOutLinearInInterpolator;
import android.support.v4.view.animation.LinearOutSlowInInterpolator;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.MotionEvent;
import android.view.View;
import android.widget.FrameLayout;
import com.kabouzeid.gramophone.R;
import com.kabouzeid.gramophone.util.Util;
/**
* Defines a basic widget that will allow for fast scrolling a RecyclerView using the basic paradigm of
* a handle and a bar.
*/
public class FastScroller extends FrameLayout {
/**
* The long bar along which a handle travels
*/
protected final View mBar;
/**
* The handle that signifies the user's progress in the list
*/
protected final View mHandle;
protected RecyclerView.OnScrollListener mOnScrollListener;
protected OnTouchListener mOnTouchListener;
private RecyclerView mRecyclerView;
private AnimatorSet mAnimator;
private boolean animatingIn;
private final Runnable mHide;
private final int mMinScrollHandleHeight;
private final int mHiddenTranslationX;
public FastScroller(Context context) {
this(context, null, 0);
}
public FastScroller(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public FastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
super(context, attrs, defStyleAttr);
inflate(context, R.layout.vertical_recycler_fast_scroller_layout, this);
mBar = findViewById(R.id.scroll_bar);
mHandle = findViewById(R.id.scroll_handle);
mMinScrollHandleHeight = getResources().getDimensionPixelSize(R.dimen.min_scrollhandle_height);
mHiddenTranslationX = (Util.isRTL(getContext()) ? -1 : 1) * getResources().getDimensionPixelSize(R.dimen.scrollbar_width);
mHide = new Runnable() {
@Override
public void run() {
if (!mHandle.isPressed()) {
if (mAnimator != null && mAnimator.isStarted()) {
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
ObjectAnimator animator2 = ObjectAnimator.ofFloat(FastScroller.this, View.TRANSLATION_X,
mHiddenTranslationX);
animator2.setInterpolator(new FastOutLinearInInterpolator());
animator2.setDuration(150);
mHandle.setEnabled(false);
mAnimator.play(animator2);
mAnimator.start();
}
}
};
mHandle.setOnTouchListener(new OnTouchListener() {
private float mInitialBarHeight;
private float mLastPressedYAdjustedToInitial;
@Override
public boolean onTouch(View v, MotionEvent event) {
mOnTouchListener.onTouch(v, event);
if (event.getActionMasked() == MotionEvent.ACTION_DOWN) {
mHandle.setPressed(true);
mInitialBarHeight = mBar.getHeight();
mLastPressedYAdjustedToInitial = event.getY() + mHandle.getY() + mBar.getY();
} else if (event.getActionMasked() == MotionEvent.ACTION_MOVE) {
float newHandlePressedY = event.getY() + mHandle.getY() + mBar.getY();
int barHeight = mBar.getHeight();
float newHandlePressedYAdjustedToInitial =
newHandlePressedY + (mInitialBarHeight - barHeight);
float deltaPressedYFromLastAdjustedToInitial =
newHandlePressedYAdjustedToInitial - mLastPressedYAdjustedToInitial;
int dY = (int) ((deltaPressedYFromLastAdjustedToInitial / mInitialBarHeight) *
(mRecyclerView.computeVerticalScrollRange()));
updateRvScroll(dY);
mLastPressedYAdjustedToInitial = newHandlePressedYAdjustedToInitial;
} else if (event.getActionMasked() == MotionEvent.ACTION_UP) {
mLastPressedYAdjustedToInitial = -1;
mHandle.setPressed(false);
postAutoHide();
}
return true;
}
});
setTranslationX(mHiddenTranslationX);
//Default selected handle color
setPressedHandleColor(Color.BLACK);
setUpBarBackground();
}
/**
* Provides the ability to programmatically set the color of the fast scroller's handle
*/
public void setPressedHandleColor(int accent) {
StateListDrawable drawable = new StateListDrawable();
int colorControlNormal = Util.resolveColor(getContext(), R.attr.colorControlNormal);
if (!Util.isRTL(getContext())) {
drawable.addState(View.PRESSED_ENABLED_STATE_SET,
new InsetDrawable(new ColorDrawable(accent), getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0, 0));
drawable.addState(View.EMPTY_STATE_SET,
new InsetDrawable(new ColorDrawable(colorControlNormal), getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0, 0));
} else {
drawable.addState(View.PRESSED_ENABLED_STATE_SET,
new InsetDrawable(new ColorDrawable(accent), 0, getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0));
drawable.addState(View.EMPTY_STATE_SET,
new InsetDrawable(new ColorDrawable(colorControlNormal), 0, getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0));
}
mHandle.setBackground(drawable);
}
private void setUpBarBackground() {
Drawable drawable;
int colorControlNormal = Util.resolveColor(getContext(), R.attr.colorControlNormal);
if (!Util.isRTL(getContext())) {
drawable = new InsetDrawable(new ColorDrawable(colorControlNormal), getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0, 0);
} else {
drawable = new InsetDrawable(new ColorDrawable(colorControlNormal), 0, getResources().getDimensionPixelSize(R.dimen.scrollbar_inset), 0, 0);
}
mBar.setBackground(drawable);
}
public void setRecyclerView(RecyclerView recyclerView) {
mRecyclerView = recyclerView;
initRecyclerViewOnScrollListener();
}
public void initRecyclerViewOnScrollListener() {
mOnScrollListener = new RecyclerView.OnScrollListener() {
@Override
public void onScrolled(RecyclerView recyclerView, int dx, int dy) {
super.onScrolled(recyclerView, dx, dy);
requestLayout();
mHandle.setEnabled(true);
if (!animatingIn && getTranslationX() != 0) {
if (mAnimator != null && mAnimator.isStarted()) {
mAnimator.cancel();
}
mAnimator = new AnimatorSet();
ObjectAnimator animator = ObjectAnimator.ofFloat(FastScroller.this, View.TRANSLATION_X, 0);
animator.setInterpolator(new LinearOutSlowInInterpolator());
animator.setDuration(100);
animator.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationEnd(Animator animation) {
super.onAnimationEnd(animation);
animatingIn = false;
}
});
animatingIn = true;
mAnimator.play(animator);
mAnimator.start();
}
postAutoHide();
}
};
mRecyclerView.addOnScrollListener(mOnScrollListener);
}
public void setOnHandleTouchListener(OnTouchListener listener) {
mOnTouchListener = listener;
}
private void postAutoHide() {
if (mRecyclerView != null) {
mRecyclerView.removeCallbacks(mHide);
mRecyclerView.postDelayed(mHide, 1500);
}
}
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
int scrollOffset = mRecyclerView.computeVerticalScrollOffset();
int verticalScrollRange = mRecyclerView.computeVerticalScrollRange()
+ mRecyclerView.getPaddingBottom();
int barHeight = mBar.getHeight();
float ratio = (float) scrollOffset / (verticalScrollRange - barHeight);
int calculatedHandleHeight = (int) ((float) barHeight / verticalScrollRange * barHeight);
if (calculatedHandleHeight < mMinScrollHandleHeight) {
calculatedHandleHeight = mMinScrollHandleHeight;
}
if (calculatedHandleHeight >= barHeight) {
setTranslationX(mHiddenTranslationX);
return;
}
float y = ratio * (barHeight - calculatedHandleHeight);
mHandle.layout(mHandle.getLeft(), (int) y, mHandle.getRight(), (int) y + calculatedHandleHeight);
}
public void updateRvScroll(int dY) {
if (mRecyclerView != null && mHandle != null) {
try {
mRecyclerView.scrollBy(0, dY);
} catch (Throwable t) {
t.printStackTrace();
}
}
}
}

View file

@ -1,12 +1,12 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:background="?android:colorBackground"
android:layout_width="match_parent"
android:layout_height="@dimen/header_image_height">
android:layout_height="@dimen/header_image_height"
android:background="?android:colorBackground">
<ImageView
android:id="@+id/album_art_background"
@ -98,6 +98,6 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="end|right|bottom"
android:layout_margin="16dp" />
android:layout_margin="@dimen/fab_margin" />
</FrameLayout>

View file

@ -1,10 +1,10 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<com.github.ksoichiro.android.observablescrollview.ObservableScrollView
android:id="@+id/observableScrollView"
@ -26,6 +26,7 @@
android:id="@+id/header"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@+id/image"
android:elevation="@dimen/toolbar_elevation"
android:minHeight="@dimen/title_view_height"
android:orientation="vertical"
@ -33,8 +34,7 @@
android:paddingLeft="16dp"
android:paddingRight="16dp"
android:paddingTop="8dp"
tools:ignore="UnusedAttribute"
android:layout_below="@+id/image">
tools:ignore="UnusedAttribute">
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
@ -139,7 +139,7 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="bottom|right|end"
android:layout_margin="16dp"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done_white_24dp"
tools:ignore="RtlHardcoded" />

View file

@ -1,13 +1,13 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<FrameLayout
android:background="?android:colorBackground"
android:layout_width="match_parent"
android:layout_height="@dimen/header_image_height">
android:layout_height="@dimen/header_image_height"
android:background="?android:colorBackground">
<ImageView
android:id="@+id/artist_image_background"
@ -99,6 +99,6 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="end|right|bottom"
android:layout_margin="16dp" />
android:layout_margin="@dimen/fab_margin" />
</FrameLayout>

View file

@ -1,8 +1,8 @@
<android.support.v4.widget.DrawerLayout
android:id="@+id/drawer_layout"
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/drawer_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
@ -19,9 +19,9 @@
android:theme="@style/ThemeOverlay.AppCompat.Dark.ActionBar">
<FrameLayout
app:layout_scrollFlags="scroll|enterAlways"
android:layout_width="match_parent"
android:layout_height="wrap_content">
android:layout_height="wrap_content"
app:layout_scrollFlags="scroll|enterAlways">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
@ -59,7 +59,7 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="end|bottom"
android:layout_margin="16dp" />
android:layout_margin="@dimen/fab_margin" />
</android.support.design.widget.CoordinatorLayout>

View file

@ -1,18 +1,18 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.kabouzeid.gramophone.ui.activities.MusicControllerActivity">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context="com.kabouzeid.gramophone.ui.activities.MusicControllerActivity">
<!-- BOTTOM-->
<LinearLayout
android:background="?android:colorBackground"
android:id="@+id/footer_frame"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:background="?android:colorBackground"
android:orientation="vertical">
<RelativeLayout
@ -166,7 +166,8 @@
<android.support.design.widget.FloatingActionButton
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_centerInParent="true" />
android:layout_centerInParent="true"
android:layout_margin="@dimen/tmp_no_fab_margin" />
</RelativeLayout>

View file

@ -1,6 +1,6 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
@ -49,6 +49,6 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="bottom|end"
android:layout_margin="16dp" />
android:layout_margin="@dimen/fab_margin" />
</FrameLayout>

View file

@ -1,10 +1,10 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
android:focusable="true"
android:focusableInTouchMode="true">
<com.github.ksoichiro.android.observablescrollview.ObservableScrollView
android:id="@+id/observableScrollView"
@ -85,8 +85,7 @@
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<EditText
android:id="@+id/artist"
@ -105,8 +104,7 @@
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<EditText
android:id="@+id/genre"
@ -125,8 +123,7 @@
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<EditText
android:id="@+id/year"
@ -146,8 +143,7 @@
<android.support.design.widget.TextInputLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
>
android:layout_height="wrap_content">
<EditText
android:id="@+id/track_number"
@ -181,7 +177,7 @@
android:id="@+id/fab"
style="@style/PlayPauseFab"
android:layout_gravity="bottom|right|end"
android:layout_margin="16dp"
android:layout_margin="@dimen/fab_margin"
android:src="@drawable/ic_done_white_24dp"
tools:ignore="RtlHardcoded" />

View file

@ -1,19 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/fragment_album_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical" />
</LinearLayout>

View file

@ -1,15 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/fragment_album_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:scrollbars="vertical" />
</LinearLayout>

View file

@ -1,18 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
android:id="@+id/fragment_song_view"
xmlns:android="http://schemas.android.com/apk/res/android"
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:paddingTop="@dimen/list_padding_vertical"
android:paddingBottom="@dimen/bottom_offset_fab_activity"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:dividerHeight="0px"
android:divider="@null"
android:scrollbars="vertical" />
android:scrollbars="none" />
<TextView
android:id="@android:id/empty"
@ -21,7 +19,13 @@
android:textSize="@dimen/empty_text_size"
android:textColor="?android:textColorSecondary"
android:fontFamily="sans-serif-light"
android:text="@string/no_playlists"
android:text="@string/nothing_here"
android:layout_gravity="center" />
</FrameLayout>
<com.kabouzeid.gramophone.views.FastScroller
android:id="@+id/fast_scroller"
android:layout_gravity="end"
android:layout_width="wrap_content"
android:layout_height="match_parent" />
</FrameLayout>

View file

@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
android:id="@+id/fragment_song_view"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.RecyclerView
android:listSelector="?rect_selector"
android:id="@+id/recycler_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:clipToPadding="false"
android:dividerHeight="0px"
android:divider="@null"
android:scrollbars="vertical" />
</LinearLayout>

View file

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="48dp"
android:layout_height="match_parent"
tools:layout_gravity="end">
<View
android:id="@+id/scroll_bar"
android:layout_width="48dp"
android:layout_height="match_parent"
android:layout_gravity="end"
android:alpha="0.22352941176" />
<View
android:id="@+id/scroll_handle"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="end" />
</FrameLayout>

View file

@ -2,4 +2,7 @@
<resources>
<dimen name="status_bar_padding">25dp</dimen>
<dimen name="navigation_drawer_header_height">165dp</dimen>
<dimen name="fab_margin">16dp</dimen>
<dimen name="tmp_no_fab_margin">0dp</dimen>
</resources>

View file

@ -50,4 +50,12 @@ http://developer.android.com/guide/topics/appwidgets/index.html#CreatingLayout
<dimen name="notification_albumart_size">128dp</dimen>
<dimen name="bottom_offset_fab_activity">86dp</dimen>
<dimen name="min_scrollhandle_height">48dp</dimen>
<dimen name="scrollbar_width">8dp</dimen>
<dimen name="scrollbar_inset">40dp</dimen>
<!-- ONLY 0dp WHILE THERE IS THE BUG IN DESIGN SUPPORT LIBRARY 22.2.0-->
<dimen name="fab_margin">0dp</dimen>
<dimen name="tmp_no_fab_margin">-16dp</dimen>
</resources>

View file

@ -140,6 +140,10 @@
<string name="update_image">Update Image</string>
<string name="playlist_empty_text">This playlist is empty</string>
<string name="no_playlists">No playlists</string>
<string name="no_albums">No albums</string>
<string name="no_songs">No songs</string>
<string name="no_artists">No artists</string>
<string name="nothing_here">Nothing here</string>
<string name="playlist_name">Playlist name</string>
<string name="song">Song</string>
<string name="pref_only_lollipop">Only available on Lollipop.</string>