app/src/main/java/com/andryr/musicplayer/model/Playlist.java
public class Playlist {
    private long id;
    private String name;

    public Playlist(long id, String name) {
        super();
        this.id = id;
        this.name = name;
    }

    public long getId() {
        return id;
    }

    public String getName() {
        return name;
    }


}
app/src/main/java/com/andryr/musicplayer/loaders/SongLoader.java
public class SongLoader extends BaseLoader<List<Song>> {

    private static final String[] sProjection = {MediaStore.Audio.Media._ID,
            MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST,
            MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ALBUM_ID,
            MediaStore.Audio.Media.ARTIST_ID, MediaStore.Audio.Media.TRACK,
            MediaStore.Audio.Media.DURATION
    };

    private List<Song> mSongList = null;

    public SongLoader(Context context) {
        super(context);

    }

    @Override
    public List<Song> loadInBackground() {
        mSongList = new ArrayList<>();

        Cursor cursor = getSongCursor();
        if (cursor != null && cursor.moveToFirst()) {
            int idCol = cursor
                    .getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
            if (idCol == -1) {
                idCol = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
            }
            int titleCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.TITLE);
            int artistCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ARTIST);
            int albumCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ALBUM);
            int albumIdCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
            int trackCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.TRACK);
            int durationCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.DURATION);

            do {
                long id = cursor.getLong(idCol);
                String title = cursor.getString(titleCol);

                String artist = cursor.getString(artistCol);

                String album = cursor.getString(albumCol);

                long albumId = cursor.getLong(albumIdCol);

                int track = cursor.getInt(trackCol);

                long duration = cursor.getLong(durationCol);


                mSongList.add(new Song(id, title, artist, album, albumId, track, duration));
            } while (cursor.moveToNext());

         /*   Collections.sort(mSongList, new Comparator<Song>() {

                @Override
                public int compare(Song lhs, Song rhs) {
                    Collator c = Collator.getInstance(Locale.getDefault());
                    c.setStrength(Collator.PRIMARY);
                    return c.compare(lhs.getTitle(), rhs.getTitle());
                }
            });*/

        }

        if (cursor != null) {
            cursor.close();
        }
        Log.e("test", "  d " + mSongList.size());

        return mSongList;
    }

    protected Uri getContentUri() {
        return MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
    }

    protected Cursor getSongCursor() {

        Uri musicUri = getContentUri();

        String selection = getSelectionString();
        String[] selectionArgs = getSelectionArgs();

        selection = DatabaseUtilsCompat.concatenateWhere(selection, MediaStore.Audio.Media.IS_MUSIC + " = 1");

        String fieldName = MediaStore.Audio.Media.TITLE;
        String filter = getFilter();
        return getCursor(musicUri, sProjection, selection, selectionArgs, fieldName, filter);
    }


}
app/src/main/java/com/andryr/musicplayer/fragments/dialog/PlaylistPicker.java
public class PlaylistPicker extends DialogFragment {

    private static final String[] sProjection = {
            MediaStore.Audio.Playlists._ID, MediaStore.Audio.Playlists.NAME};

    private RecyclerView mRecyclerView;

    private PlaylistListAdapter mAdapter;

    private OnPlaylistPickedListener mListener;

    private LoaderCallbacks<Cursor> mLoaderCallbacks = new LoaderCallbacks<Cursor>() {

        @Override
        public void onLoaderReset(Loader<Cursor> loader) {
            // TODO Auto-generated method stub

        }

        @Override
        public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
            List<Playlist> list = new ArrayList<>();
            if (cursor != null && cursor.moveToFirst()) {
                int idCol = cursor.getColumnIndex(MediaStore.Audio.Genres._ID);
                int nameCol = cursor
                        .getColumnIndex(MediaStore.Audio.Genres.NAME);

                do {
                    long id = cursor.getLong(idCol);
                    String name = cursor.getString(nameCol);
                    list.add(new Playlist(id, name));
                } while (cursor.moveToNext());

                Collections.sort(list, new Comparator<Playlist>() {

                    @Override
                    public int compare(Playlist lhs, Playlist rhs) {
                        Collator c = Collator.getInstance(Locale.getDefault());
                        c.setStrength(Collator.PRIMARY);
                        return c.compare(lhs.getName(), rhs.getName());
                    }
                });

            }

            mAdapter.setData(list);

        }

        @Override
        public Loader<Cursor> onCreateLoader(int id, Bundle args) {

            Uri playlistsUri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;

            CursorLoader loader = new CursorLoader(getActivity(), playlistsUri,
                    sProjection, null, null, null);

            return loader;
        }
    };
    private OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {
            switch (v.getId()) {
                case R.id.new_playlist:
                    CreatePlaylistDialog dialog = CreatePlaylistDialog.newInstance();
                    dialog.setOnPlaylistCreatedListener(new CreatePlaylistDialog.OnPlaylistCreatedListener() {
                        @Override
                        public void onPlaylistCreated() {
                            refresh();
                        }
                    });
                    dialog.show(getChildFragmentManager(), "create_playlist");
                    break;
            }


        }
    };
    private BaseAdapter.OnItemClickListener mOnItemClickListener = new BaseAdapter.OnItemClickListener() {
        @Override
        public void onItemClick(int position, View view) {
            Playlist playlist = mAdapter.getItem(position);

            if (mListener != null) {
                mListener.onPlaylistPicked(playlist);
            }

            dismiss();
        }
    };

    public PlaylistPicker() {
        // Required empty public constructor
    }

    public static PlaylistPicker newInstance() {
        PlaylistPicker fragment = new PlaylistPicker();

        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        getLoaderManager().initLoader(0, null, mLoaderCallbacks);
    }


    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        android.support.v7.app.AlertDialog.Builder builder = new android.support.v7.app.AlertDialog.Builder(getActivity(), getTheme());
        mAdapter = new PlaylistListAdapter();
        mAdapter.setOnItemClickListener(mOnItemClickListener);

        builder.setTitle(R.string.choose_playlist);

        View rootView = LayoutInflater.from(getActivity()).inflate(R.layout.fragment_playlist_picker, null);
        mRecyclerView = (RecyclerView) rootView.findViewById(R.id.recycler_view);
        mRecyclerView.setLayoutManager(new LinearLayoutManager(getActivity()));

        mRecyclerView.setAdapter(mAdapter);


        Button newPlaylistButton = (Button) rootView.findViewById(R.id.new_playlist);
        newPlaylistButton.setOnClickListener(mOnClickListener);
        ThemeHelper.tintCompoundDrawables(getActivity(), newPlaylistButton);

        builder.setView(rootView);
        return builder.create();
    }


    public void refresh() {
        getLoaderManager().restartLoader(0, null, mLoaderCallbacks);

    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);

    }

    @Override
    public void onDetach() {
        super.onDetach();
    }

    public void setListener(OnPlaylistPickedListener listener) {
        mListener = listener;
    }

    public interface OnPlaylistPickedListener {
        void onPlaylistPicked(Playlist playlist);
    }


}
app/src/main/java/com/andryr/musicplayer/fragments/dialog/ID3TagEditorDialog.java
public class ID3TagEditorDialog extends DialogFragment {

    private static final String ARG_ID = "id";
    private static final String ARG_TITLE = "title";
    private static final String ARG_ARTIST = "artist";
    private static final String ARG_ALBUM = "album";
    private static final String ARG_ALBUM_ID = "album_id";
    private static final String ARG_TRACK_NUMBER = "track_number";


    private Song mSong;


    private EditText mTitleEditText;
    private EditText mArtistEditText;
    private EditText mAlbumEditText;
    private EditText mTrackEditText;
    private EditText mGenreEditText;
    private OnTagsEditionSuccessListener mListener;


    public ID3TagEditorDialog() {
        // Required empty public constructor
    }

    public static ID3TagEditorDialog newInstance(Song song) {
        ID3TagEditorDialog fragment = new ID3TagEditorDialog();
        Bundle args = new Bundle();
        args.putLong(ARG_ID, song.getId());
        args.putString(ARG_TITLE, song.getTitle());
        args.putString(ARG_ARTIST, song.getArtist());
        args.putString(ARG_ALBUM, song.getAlbum());
        args.putLong(ARG_ALBUM_ID, song.getAlbumId());
        args.putInt(ARG_TRACK_NUMBER, song.getTrackNumber());
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        Bundle args = getArguments();
        if (args != null) {

            long id = args.getLong(ARG_ID);
            String title = args.getString(ARG_TITLE);
            String artist = args.getString(ARG_ARTIST);
            String album = args.getString(ARG_ALBUM);
            long albumId = args.getLong(ARG_ALBUM_ID);
            int trackNumber = args.getInt(ARG_TRACK_NUMBER);
            mSong = new Song(id, title, artist, album, albumId, trackNumber, 0);


        }
    }


    @Override
    public Dialog onCreateDialog(Bundle savedInstanceState) {
        AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
        builder.setTitle(R.string.edit_tags);

        mSong.setGenre(MusicLibraryHelper.getSongGenre(getActivity(), mSong.getId()));//TODO récupérer le genre de manière asynchrone ??
        View dialogView = getActivity().getLayoutInflater().inflate(R.layout.fragment_id3_tag_editor_dialog, null);
        builder.setView(dialogView);

        mTitleEditText = (EditText) dialogView.findViewById(R.id.title);
        mArtistEditText = (EditText) dialogView.findViewById(R.id.artist);
        mAlbumEditText = (EditText) dialogView.findViewById(R.id.album);
        mTrackEditText = (EditText) dialogView.findViewById(R.id.track_number);
        mGenreEditText = (EditText) dialogView.findViewById(R.id.genre);

        mTitleEditText.setText(mSong.getTitle());
        mArtistEditText.setText(mSong.getArtist());
        mAlbumEditText.setText(mSong.getAlbum());
        mTrackEditText.setText(String.valueOf(mSong.getTrackNumber()));
        mGenreEditText.setText(mSong.getGenre());


        builder.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {

                final Activity activity = getActivity();
                dismiss();

                new AsyncTask<Object,Object,Boolean>(){

                    @Override
                    protected Boolean doInBackground(Object... params) {
                        return saveTags(activity);
                    }

                    @Override
                    protected void onPostExecute(Boolean b) {
                        super.onPostExecute(b);
                        if (b) {
                            if(mListener != null)
                            {
                                mListener.onTagsEditionSuccess();
                            }
                        }
                        else
                        {
                            Toast.makeText(getContext(),R.string.tags_edition_failed,Toast.LENGTH_SHORT).show();

                        }


                    }
                }.execute();


            }
        }).setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {
            @Override
            public void onClick(DialogInterface dialog, int which) {
                dismiss();
            }
        });


        return builder.create();
    }

    private boolean saveTags(Context context) {

        HashMap<String,String> tags = new HashMap<>();

        tags.put(MusicLibraryHelper.TITLE, mTitleEditText.getText().toString());
        tags.put(MusicLibraryHelper.ARTIST, mArtistEditText.getText().toString());
        tags.put(MusicLibraryHelper.ALBUM, mAlbumEditText.getText().toString());
        tags.put(MusicLibraryHelper.TRACK, mTrackEditText.getText().toString());
        tags.put(MusicLibraryHelper.GENRE, mGenreEditText.getText().toString());




        return MusicLibraryHelper.editSongTags(context, mSong, tags);

    }


    public void setOnTagsEditionSuccessListener(OnTagsEditionSuccessListener listener)
    {
        mListener = listener;
    }


    public interface OnTagsEditionSuccessListener {


        void onTagsEditionSuccess();
    }


}
app/src/main/java/com/andryr/musicplayer/utils/PrefUtils.java
public class PrefUtils {


    private static final String SONG_SORT_ORDER = "song_sort_order";
    public static final String ARTIST_SORT_ORDER = "artist_sort_order";
    public static final String ALBUM_SORT_ORDER = "album_sort_order";


    private static PrefUtils sInstance = null;

    private Context mContext;

    private SharedPreferences mPreferences;

    private PrefUtils(Context context) {
        mContext = context;
        mPreferences = PreferenceManager.getDefaultSharedPreferences(context);
    }

    public static void init(Context context) {
        sInstance = new PrefUtils(context);
    }

    public static PrefUtils getInstance() {
        return sInstance;
    }

    public void putString(String key, String value) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putString(key, value);
        editor.apply();
    }

    public void putInt(String key, int value) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putInt(key, value);
        editor.apply();
    }

    public void putLong(String key, long value) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putLong(key, value);
        editor.apply();
    }

    public void putBoolean(String key, boolean value) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putBoolean(key, value);
        editor.apply();
    }

    public void putFloat(String key, float value) {
        SharedPreferences.Editor editor = mPreferences.edit();
        editor.putFloat(key, value);
        editor.apply();
    }


    public void setSongSortOrder(String value) {
        putString(SONG_SORT_ORDER, value);
    }

    public void setArtistSortOrder(String value) {
        putString(ARTIST_SORT_ORDER, value);
    }

    public void setAlbumSortOrder(String value) {
        putString(ALBUM_SORT_ORDER, value);
    }

    public String getSongSortOrder() {
        return mPreferences.getString(SONG_SORT_ORDER, SortOrder.SongSortOrder.SONG_A_Z);
    }

    public String getArtistSortOrder() {
        return mPreferences.getString(ARTIST_SORT_ORDER, SortOrder.ArtistSortOrder.ARTIST_A_Z);
    }

    public String getAlbumSortOrder() {
        return mPreferences.getString(ALBUM_SORT_ORDER, SortOrder.AlbumSortOrder.ALBUM_A_Z);
    }

    public boolean useFreeArtworks() {
        String prefKey = mContext.getString(R.string.pref_use_free_artworks_key);
        return mPreferences.getBoolean(prefKey, false);
    }
}
app/src/main/java/com/andryr/musicplayer/adapters/AdapterWithHeader.java
public abstract class AdapterWithHeader<VH extends RecyclerView.ViewHolder> extends BaseAdapter<VH> {


    private static final int VIEW_TYPE_HEADER = 433;
    private View mHeaderView;
    private OnHeaderClickListener mOnHeaderClickListener;

    private int mHeaderLayoutId;
    private boolean mHeaderSet = false;


    public void setHeaderView(View view) {
        mHeaderView = view;
        mHeaderSet = true;
        notifyDataSetChanged();
    }
    public void setHeaderLayout(int layoutId) {
        mHeaderLayoutId = layoutId;
        mHeaderSet = true;
        notifyDataSetChanged();
    }

    public void setOnHeaderClickListener(OnHeaderClickListener listener) {
        mOnHeaderClickListener = listener;
    }

    @Override
    public VH onCreateViewHolder(ViewGroup parent, int viewType) {
        if (viewType == VIEW_TYPE_HEADER) {
            if(mHeaderView == null) {
                mHeaderView = LayoutInflater.from(parent.getContext()).inflate(
                        mHeaderLayoutId, parent, false);
            }
            return (VH) new HeaderViewHolder(mHeaderView);
        }
        return onCreateViewHolderImpl(parent, viewType);
    }

    @Override
    public void onBindViewHolder(VH holder, int position) {
        if (position >= 1 || mHeaderView == null) {
            onBindViewHolderImpl(holder, position - (mHeaderView != null ? 1 : 0));
        }
    }

    @Override
    public int getItemCount() {
        return getItemCountImpl() + (mHeaderView != null ? 1 : 0);
    }

    @Override
    public int getItemViewType(int position) {
        if (position == 0 && mHeaderSet) {
            return VIEW_TYPE_HEADER;
        }
        return getItemViewTypeImpl(position - (mHeaderView != null ? 1 : 0));
    }

    @Override
    protected void triggerOnItemClickListener(int position, View view) {
        super.triggerOnItemClickListener(position - (mHeaderView != null ? 1 : 0), view);
    }

    public abstract VH onCreateViewHolderImpl(ViewGroup parent, int viewType);

    public abstract void onBindViewHolderImpl(VH holder, int position);

    public abstract int getItemCountImpl();

    public abstract int getItemViewTypeImpl(int position);

    public interface OnHeaderClickListener {
        void onHeaderClick();
    }

    private class HeaderViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        public HeaderViewHolder(View itemView) {
            super(itemView);
            itemView.setOnClickListener(this);
        }

        @Override
        public void onClick(View v) {
            if (mOnHeaderClickListener != null) {
                mOnHeaderClickListener.onHeaderClick();
            }
        }
    }
}
app/src/main/java/com/andryr/musicplayer/MainActivity.java
public class MainActivity extends AppCompatActivity {
    public static final String ALBUM_ID = "id";
    public static final String ALBUM_NAME = "name";
    public static final String ALBUM_ARTIST = "artist";
    public static final String ALBUM_YEAR = "year";
    public static final String ALBUM_TRACK_COUNT = "track_count";
    public static final String ARTIST_ARTIST_ID = "artist_id";
    public static final String ARTIST_ARTIST_NAME = "artist_name";
    public static final String ARTIST_ALBUM_COUNT = "album_count";
    public static final String ARTIST_TRACK_COUNT = "track_count";
    public static final String SONG_ID = "song_id";
    public static final String SONG_TITLE = "song_title";
    public static final String SONG_ARTIST = "song_artist";
    public static final String SONG_ALBUM = "song_album";
    public static final String SONG_ALBUM_ID = "song_album_id";
    public static final String SONG_TRACK_NUMBER = "song_track_number";
    private static final String SONG_DURATION = "song_duration";

    public static final String ACTION_REFRESH = "resfresh";
    public static final String ACTION_SHOW_ALBUM = "show_album";
    public static final String ACTION_SHOW_ARTIST = "show_artist";
    public static final String ACTION_PLAY_SONG = "play_song";
    public static final String ACTION_ADD_TO_QUEUE = "add_to_queue";
    public static final String ACTION_SET_AS_NEXT_TRACK = "set_as_next_track";

    private static final int SEARCH_ACTIVITY = 234;

    private static final int PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE = 1;
    private static final int PERMISSIONS_REQUEST_READ_PHONE_STATE = 2;
    private static final int PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE = 3;

    private Intent mOnActivityResultIntent;
    private PlaybackService mPlaybackService;
    private Intent mServiceIntent;
    private boolean mServiceBound = false;

    private ProgressBar mProgressBar;
    private Handler mHandler = new Handler();

    private int mThumbSize;


    private PlaybackRequests mPlaybackRequests;
    private OnClickListener mOnClickListener = new OnClickListener() {

        @Override
        public void onClick(View v) {

            if (mPlaybackService == null) {
                return;
            }
            switch (v.getId()) {
                case R.id.play_pause_toggle:
                case R.id.quick_play_pause_toggle:

                    mPlaybackService.toggle();

                    break;
                case R.id.quick_prev:
                case R.id.prev:
                    mPlaybackService.playPrev(true);

                    break;
                case R.id.quick_next:
                case R.id.next:
                    mPlaybackService.playNext(true);
                    break;


                case R.id.action_equalizer:
                    NavigationUtils.showEqualizer(MainActivity.this);
                    break;
                case R.id.track_info:
                    NavigationUtils.showPlaybackActivity(MainActivity.this);
                    break;


            }

        }
    };
    private Runnable mUpdateProgressBar = new Runnable() {

        @Override
        public void run() {


            updateProgressBar();


            mHandler.postDelayed(mUpdateProgressBar, 1000);

        }
    };
    private NavigationView mNavigationView;
    private DrawerLayout mDrawerLayout;
    private ServiceConnection mServiceConnection = new ServiceConnection() {

        @Override
        public void onServiceConnected(ComponentName name, IBinder service) {

            PlaybackService.PlaybackBinder binder = (PlaybackBinder) service;
            mPlaybackService = binder.getService();
            mServiceBound = true;

            mPlaybackRequests.sendRequests();

            updateAll();
         /*   if (!mPlaybackService.isPlaying()
                    && !mPlaybackService.hasPlaylist()) {

                List<Song> playList = getDefaultPlaylist();
                Log.d("playlist", String.valueOf(playList == null));
                if (playList != null) {
                    Log.d("playlist", String.valueOf(playList.size()));

                    int pos = (int) (Math.random() * (playList.size() - 1));
                    Log.d("playlist", String.valueOf(pos));

                    mPlaybackService.setPlayList(playList, pos, false);
                }

            }*/

        }

        @Override
        public void onServiceDisconnected(ComponentName name) {
            mServiceBound = false;

        }
    };
    private BroadcastReceiver mServiceListener = new BroadcastReceiver() {

        @Override
        public void onReceive(Context context, Intent intent) {
            if (mPlaybackService == null) {
                return;
            }
            String action = intent.getAction();
            Log.d("action", action);
            if (action.equals(PlaybackService.PLAYSTATE_CHANGED)) {
                setButtonDrawable();
                if (mPlaybackService.isPlaying()) {
                    mHandler.post(mUpdateProgressBar);
                } else {
                    mHandler.removeCallbacks(mUpdateProgressBar);
                }


            } else if (action.equals(PlaybackService.META_CHANGED)) {
                updateTrackInfo();
            }

        }
    };


    /**
     * Handler for the sleep timer dialog
     */
    private HmsPickerDialogFragment.HmsPickerDialogHandler mHmsPickerHandler = new HmsPickerDialogFragment.HmsPickerDialogHandler() {
        @Override
        public void onDialogHmsSet(int reference, int hours, int minutes, int seconds) {
            SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
            SleepTimer.setTimer(MainActivity.this, prefs, hours * 3600 + minutes * 60 + seconds);
        }
    };


    private DialogInterface.OnClickListener mSleepTimerDialogListener = new DialogInterface.OnClickListener() {
        @Override
        public void onClick(DialogInterface dialog, int which) {
            switch(which) {
                case DialogInterface.BUTTON_POSITIVE: // set a new timer
                    DialogUtils.showSleepHmsPicker(MainActivity.this, mHmsPickerHandler);
                    break;
                case DialogInterface.BUTTON_NEGATIVE: // cancel the current timer
                    SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(MainActivity.this);
                    SleepTimer.cancelTimer(MainActivity.this, prefs);
                    break;
                case DialogInterface.BUTTON_NEUTRAL: // just go back
                    break;

            }
        }
    };

    public DrawerLayout getDrawerLayout() {
        return mDrawerLayout;
    }

  /*  private List<Song> getDefaultPlaylist() {
        ContentResolver resolver = getContentResolver();
        Uri musicUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
        String[] projection = {MediaStore.Audio.Media._ID,
                MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.ARTIST,
                MediaStore.Audio.Media.ALBUM, MediaStore.Audio.Media.ALBUM_ID,
                MediaStore.Audio.Media.ARTIST_ID, MediaStore.Audio.Media.TRACK};
        Cursor cursor = resolver.query(musicUri, projection, null, null,
                android.provider.MediaStore.Audio.Media.TITLE);

        List<Song> songList = new ArrayList<>();
        if (cursor != null && cursor.moveToFirst()) {
            int idCol = cursor
                    .getColumnIndex(MediaStore.Audio.Playlists.Members.AUDIO_ID);
            if (idCol == -1) {
                idCol = cursor.getColumnIndex(MediaStore.Audio.Media._ID);
            }
            int titleCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.TITLE);
            int artistCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ARTIST);
            int albumCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ALBUM);
            int albumIdCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.ALBUM_ID);
            int trackCol = cursor
                    .getColumnIndex(MediaStore.Audio.Media.TRACK);

            do {
                long id = cursor.getLong(idCol);
                String title = cursor.getString(titleCol);

                String artist = cursor.getString(artistCol);

                String album = cursor.getString(albumCol);

                long albumId = cursor.getLong(albumIdCol);

                int track = cursor.getInt(trackCol);


                songList.add(new Song(id, title, artist, album, albumId, track));
            } while (cursor.moveToNext());
        }
        if (cursor != null) {
            cursor.close();
        }
        return songList;

    }*/

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        setTheme();

        super.onCreate(savedInstanceState);

        setVolumeControlStream(AudioManager.STREAM_MUSIC);

        setContentView(R.layout.activity_main);


        mThumbSize = getResources().getDimensionPixelSize(R.dimen.art_thumbnail_size);


        mPlaybackRequests = new PlaybackRequests();

        if (savedInstanceState == null) {
            showLibrary();
        }


        findViewById(R.id.quick_play_pause_toggle).setOnClickListener(
                mOnClickListener);

        findViewById(R.id.track_info).setOnClickListener(mOnClickListener);

        findViewById(R.id.quick_prev).setOnClickListener(mOnClickListener);
        findViewById(R.id.quick_next).setOnClickListener(mOnClickListener);


        mProgressBar = (ProgressBar) findViewById(R.id.progress_bar);

        mDrawerLayout = (DrawerLayout) findViewById(R.id.drawer_layout);
        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);
        mNavigationView.inflateHeaderView(R.layout.navigation_header);
        mNavigationView.setNavigationItemSelectedListener(new NavigationView.OnNavigationItemSelectedListener() {
            @Override
            public boolean onNavigationItemSelected(MenuItem menuItem) {
                mDrawerLayout.closeDrawers();
                switch (menuItem.getItemId()) {
                   /* case R.id.action_home:
                        showHome();
                        break;*/
                    case R.id.action_library:
                        showLibrary();
                        break;
                    case R.id.action_favorites:
                        showFavorites();
                        break;
                    case R.id.action_equalizer:
                        NavigationUtils.showEqualizer(MainActivity.this);
                        break;
                    case R.id.action_settings:
                        NavigationUtils.showPreferencesActivity(MainActivity.this);
                        break;
                }
                return true;
            }
        });
        checkPermissions();

    }

    private void setTheme() {
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);

        boolean dark = ThemeHelper.isDarkThemeSelected(this);
        String prefKey = getString(R.string.pref_theme_key);
        int theme = prefs.getInt(prefKey, ThemePreference.DEFAULT_THEME);

        switch (theme) {
            case ThemePreference.DARK_BLUE_GREY_THEME:
                if (dark) {
                    setTheme(R.style.MainActivityDarkBlueGreyDark);
                } else {
                    setTheme(R.style.MainActivityDarkBlueGreyLight);
                }
                break;
            case ThemePreference.BLUE_GREY_THEME:
                if (dark) {
                    setTheme(R.style.MainActivityBlueGreyDark);
                } else {
                    setTheme(R.style.MainActivityBlueGreyLight);
                }
                break;
            case ThemePreference.BLUE_THEME:
                if (dark) {
                    setTheme(R.style.MainActivityBlueDark);
                } else {
                    setTheme(R.style.MainActivityBlueLight);
                }
                break;
        }
    }

    /**
     * Affiche bibliothèque sans backstack
     */
    public void showLibrary() {
        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);

        mNavigationView.getMenu().findItem(R.id.action_library).setChecked(true);
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container, LibraryFragment.newInstance()).commit();
    }

    /**
     * Afficher favoris sans backstack
     */
    public void showFavorites() {
        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);

        mNavigationView.getMenu().findItem(R.id.action_favorites).setChecked(true);
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container, PlaylistFragment.newFavoritesFragment()).commit();
    }

    private void checkPermissions() {
        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.READ_EXTERNAL_STORAGE)) {

                DialogUtils.showPermissionDialog(this, getString(R.string.permission_read_external_storage), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                                PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);
                    }
                });

            } else {


                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_EXTERNAL_STORAGE},
                        PERMISSIONS_REQUEST_READ_EXTERNAL_STORAGE);


            }
        }

        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.WRITE_EXTERNAL_STORAGE)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.WRITE_EXTERNAL_STORAGE)) {

                DialogUtils.showPermissionDialog(this, getString(R.string.permission_write_external_storage), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                                PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);
                    }
                });

            } else {


                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE},
                        PERMISSIONS_REQUEST_WRITE_EXTERNAL_STORAGE);


            }
        }

        if (ContextCompat.checkSelfPermission(this,
                Manifest.permission.READ_PHONE_STATE)
                != PackageManager.PERMISSION_GRANTED) {

            if (ActivityCompat.shouldShowRequestPermissionRationale(this,
                    Manifest.permission.READ_PHONE_STATE)) {

                DialogUtils.showPermissionDialog(this, getString(R.string.permission_read_phone_state), new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        ActivityCompat.requestPermissions(MainActivity.this,
                                new String[]{Manifest.permission.READ_PHONE_STATE},
                                PERMISSIONS_REQUEST_READ_PHONE_STATE);
                    }
                });

            } else {


                ActivityCompat.requestPermissions(this,
                        new String[]{Manifest.permission.READ_PHONE_STATE},
                        PERMISSIONS_REQUEST_READ_PHONE_STATE);


            }
        }
    }

    @Override

    protected void onStop() {


        super.onStop();
    }

   /* private void showHome() {
        mNavigationView = (NavigationView) findViewById(R.id.navigation_view);

        mNavigationView.getMenu().findItem(R.id.action_home).setChecked(true);
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container, HomeFragment.newInstance()).commit();
    }*/

    @Override
    protected void onPostResume() {
        super.onPostResume();
        if (mOnActivityResultIntent != null) {
            Bundle bundle = mOnActivityResultIntent.getExtras();
            if (mOnActivityResultIntent.getAction().equals(ACTION_REFRESH)) {
                refresh();
            } else if (mOnActivityResultIntent.getAction().equals(ACTION_SHOW_ALBUM)) {
                Album album = getAlbumFromBundle(bundle);
                AlbumFragment fragment = AlbumFragment.newInstance(album);
                setFragment(fragment);
            } else if (mOnActivityResultIntent.getAction().equals(ACTION_SHOW_ARTIST)) {
                Artist artist = getArtistFromBundle(bundle);
                ArtistFragment fragment = ArtistFragment.newInstance(artist);
                setFragment(fragment);
            } else {


                Song song = getSongFromBundle(bundle);

                if (mOnActivityResultIntent.getAction().equals(ACTION_PLAY_SONG)) {
                    ArrayList<Song> songList = new ArrayList<>();
                    songList.add(song);
                    mPlaybackRequests.requestPlayList(songList, 0, true);
                } else if (mOnActivityResultIntent.getAction().equals(ACTION_ADD_TO_QUEUE)) {
                    mPlaybackRequests.requestAddToQueue(song);
                } else if (mOnActivityResultIntent.getAction().equals(ACTION_SET_AS_NEXT_TRACK)) {
                    mPlaybackRequests.requestAsNextTrack(song);
                }


            }
            mOnActivityResultIntent = null;
        }
    }

    public void refresh() {
        for (Fragment f : getSupportFragmentManager().getFragments()) {
            if (f != null) {
                Log.d("frag", f.getClass().getCanonicalName());
                ((BaseFragment) f).load();
            }
        }
    }

    private Album getAlbumFromBundle(Bundle bundle) {
        long id = bundle.getLong(ALBUM_ID);
        String title = bundle.getString(ALBUM_NAME);
        String artist = bundle.getString(ALBUM_ARTIST);
        int year = bundle.getInt(ALBUM_YEAR);
        int trackCount = bundle.getInt(ALBUM_TRACK_COUNT);

        return new Album(id, title, artist, year, trackCount);
    }

    public void setFragment(Fragment f) {
        getSupportFragmentManager().beginTransaction()
                .replace(R.id.container, f).addToBackStack(null).commit();
    }

    private Artist getArtistFromBundle(Bundle bundle) {
        long id = bundle.getLong(ARTIST_ARTIST_ID);
        String name = bundle.getString(ARTIST_ARTIST_NAME);
        int albumCount = bundle.getInt(ARTIST_ALBUM_COUNT);
        int trackCount = bundle.getInt(ARTIST_TRACK_COUNT);
        return new Artist(id, name, albumCount, trackCount);
    }

    private Song getSongFromBundle(Bundle bundle) {
        long id = bundle.getLong(SONG_ID);
        String title = bundle.getString(SONG_TITLE);
        String artist = bundle.getString(SONG_ARTIST);
        String album = bundle.getString(SONG_ALBUM);
        long albumId = bundle.getLong(SONG_ALBUM_ID);
        int trackNumber = bundle.getInt(SONG_TRACK_NUMBER);
        long duration = bundle.getLong(SONG_DURATION);


        return new Song(id, title, artist, album, albumId, trackNumber, duration);

    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        // unbindService(mServiceConnection);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        // Inflate the menu; this adds items to the action bar if it is present.
        getMenuInflater().inflate(R.menu.main, menu);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        // Handle action bar item clicks here. The action bar will
        // automatically handle clicks on the Home/Up button, so long
        // as you specify a parent activity in AndroidManifest.xml.
        int id = item.getItemId();
        switch (id) {
            case android.R.id.home:
                FragmentManager fm = getSupportFragmentManager();
                if (fm.getBackStackEntryCount() > 0) {
                    fm.popBackStack();
                } else {
                    showLibrary();
                }
                return true;
            case R.id.action_search:
                NavigationUtils.showSearchActivity(this, SEARCH_ACTIVITY);
                return true;
            case R.id.action_equalizer:
                NavigationUtils.showEqualizer(this);
                return true;
            case R.id.action_preferences:
                NavigationUtils.showPreferencesActivity(this);
                break;
            case R.id.action_sleep_timer:
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
                if (SleepTimer.isTimerSet(prefs)) {
                    DialogUtils.showSleepTimerDialog(this, mSleepTimerDialogListener);
                } else {
                    DialogUtils.showSleepHmsPicker(this, mHmsPickerHandler);
                }
                break;
        }
        return super.onOptionsItemSelected(item);
    }

    public void onSongSelected(List<Song> songList, int position) {
        if (mPlaybackService == null) {
            return;
        }
        mPlaybackService.setPlayList(songList, position, true);
        // mPlaybackService.play();

    }

    public void onShuffleRequested(List<Song> songList, boolean play) {
        if (mPlaybackService == null) {
            return;
        }
        mPlaybackService.setPlayListAndShuffle(songList, play);


    }

    public void addToQueue(Song song) {
        if (mPlaybackService != null) {
            mPlaybackService.addToQueue(song);
        }

    }

    public void setAsNextTrack(Song song) {
        if (mPlaybackService != null) {
            mPlaybackService.setAsNextTrack(song);
        }

    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == SEARCH_ACTIVITY && resultCode == RESULT_OK) {
            mOnActivityResultIntent = data;

        }
    }

    @Override
    protected void onPause() {
        super.onPause();

        if (mServiceBound) {
            mPlaybackService = null;

            unregisterReceiver(mServiceListener);

            unbindService(mServiceConnection);
            mServiceBound = false;
        }
        mHandler.removeCallbacks(mUpdateProgressBar);
    }

    @Override
    protected void onResume() {
        super.onResume();
        if (!mServiceBound) {
            mServiceIntent = new Intent(this, PlaybackService.class);
            bindService(mServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
            startService(mServiceIntent);
            IntentFilter filter = new IntentFilter();
            filter.addAction(PlaybackService.META_CHANGED);
            filter.addAction(PlaybackService.PLAYSTATE_CHANGED);
            filter.addAction(PlaybackService.POSITION_CHANGED);
            filter.addAction(PlaybackService.ITEM_ADDED);
            filter.addAction(PlaybackService.ORDER_CHANGED);
            registerReceiver(mServiceListener, filter);
        } else {
            updateAll();
        }
    }

    @Override
    protected void onStart() {
        super.onStart();


        // if (!mServiceBound) {
        // mServiceIntent = new Intent(this, PlaybackService.class);
        // bindService(mServiceIntent, mServiceConnection, BIND_AUTO_CREATE);
        // startService(mServiceIntent);
        // }

    }

    @Override
    public void onRequestPermissionsResult(int requestCode,
                                           String permissions[], int[] grantResults) {
        switch (requestCode) {
            case PERMISSIONS_REQUEST_READ_PHONE_STATE:
                SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
                SharedPreferences.Editor editor = prefs.edit();
                editor.putBoolean(PlaybackService.PREF_AUTO_PAUSE, true);
                if (mPlaybackService != null) {
                    mPlaybackService.setAutoPauseEnabled(true);
                }
                editor.commit();
                break;

        }
    }

    private void updateAll() {
        if (mPlaybackService != null) {
            Log.d("playlist", "hasplaylist " + mPlaybackService.hasPlaylist());
            updateTrackInfo();
            setButtonDrawable();
            if (mPlaybackService.isPlaying()) {
                mHandler.post(mUpdateProgressBar);
            }


        }
    }

    private void updateTrackInfo() {
        View trackInfoLayout = findViewById(R.id.track_info);

        if (mPlaybackService != null && mPlaybackService.hasPlaylist()) {

            if (trackInfoLayout.getVisibility() != View.VISIBLE) {
                trackInfoLayout.setVisibility(View.VISIBLE);
                trackInfoLayout.startAnimation(AnimationUtils.loadAnimation(this, R.anim.abc_grow_fade_in_from_bottom));
            }
            String title = mPlaybackService.getSongTitle();
            String artist = mPlaybackService.getArtistName();
            if (title != null) {
                ((TextView) findViewById(R.id.song_title)).setText(title);

            }
            if (artist != null) {
                ((TextView) findViewById(R.id.song_artist)).setText(artist);
            }

            long albumId = mPlaybackService.getAlbumId();
            final ImageView minArtworkView = (ImageView) findViewById(R.id.artwork_min);
            ArtworkCache.getInstance().loadBitmap(albumId, minArtworkView, mThumbSize, mThumbSize);


            int duration = mPlaybackService.getTrackDuration();
            if (duration != -1) {
                mProgressBar.setMax(duration);

                updateProgressBar();
            }


        } else {
            trackInfoLayout.setVisibility(View.GONE);
        }
    }

    private void setButtonDrawable() {
        if (mPlaybackService != null) {
            ImageButton quickButton = (ImageButton) findViewById(R.id.quick_play_pause_toggle);
            if (mPlaybackService.isPlaying()) {
                quickButton.setImageResource(R.drawable.ic_pause);
            } else {
                quickButton.setImageResource(R.drawable.ic_play_small);
            }
        }

    }


    private void updateProgressBar() {
        if (mPlaybackService != null) {
            int position = mPlaybackService.getPlayerPosition();
            mProgressBar.setProgress(position);


        }
    }

    private class PlaybackRequests {

        private List<Song> mPlayList;
        private int mIndex;
        private boolean mAutoPlay;

        private Song mNextTrack;

        private Song mAddToQueue;

        private void requestPlayList(List<Song> playList, int index, boolean autoPlay) {
            if (mPlaybackService != null) {
                mPlaybackService.setPlayList(playList, 0, true);
            } else {
                mPlayList = playList;
                mIndex = index;
                mAutoPlay = autoPlay;
            }
        }


        public void requestAddToQueue(Song song) {
            if (mPlaybackService != null) {
                mPlaybackService.addToQueue(song);
            } else {
                mAddToQueue = song;
            }
        }

        public void requestAsNextTrack(Song song) {
            if (mPlaybackService != null) {
                mPlaybackService.setAsNextTrack(song);
            } else {
                mNextTrack = song;
            }
        }

        public void sendRequests() {
            if (mPlaybackService == null) {
                return;
            }

            if (mPlayList != null) {
                mPlaybackService.setPlayList(mPlayList, mIndex, mAutoPlay);
                mPlayList = null;
            }

            if (mAddToQueue != null) {
                mPlaybackService.addToQueue(mAddToQueue);
                mAddToQueue = null;
            }

            if (mNextTrack != null) {
                mPlaybackService.setAsNextTrack(mNextTrack);
                mNextTrack = null;
            }
        }
    }

}
app/src/main/java/com/andryr/musicplayer/utils/RecyclerViewUtils.java
public class RecyclerViewUtils {

    public static View inflateChild(LayoutInflater inflater, int layoutId, RecyclerView recyclerView) {
        View view = inflater.inflate(layoutId, recyclerView,
                false);
        ViewGroup.LayoutParams lp = view.getLayoutParams();
        view.setLayoutParams(recyclerView.getLayoutManager().generateLayoutParams(lp));
        return view;
    }
}
app/src/main/java/com/andryr/musicplayer/model/Song.java
public class Song {
    private long id;
    private String title;
    private String artist;
    private String album;
    private int trackNumber;
    private long albumId;
    private String genre;
    private long duration;

    public Song(long id, String title, String artist, String album, long albumId, int trackNumber, long duration) {
        super();
        this.id = id;
        this.title = title == null ? MediaStore.UNKNOWN_STRING : title;
        this.artist = artist == null ? MediaStore.UNKNOWN_STRING : artist;
        this.album = album == null ? MediaStore.UNKNOWN_STRING : album;
        this.albumId = albumId;
        this.trackNumber = trackNumber;
        this.duration = duration;

    }

    public long getId() {
        return id;
    }

    public String getAlbum() {
        return album;
    }

    public String getTitle() {
        return title;
    }

    public String getArtist() {
        return artist;
    }

    public long getAlbumId() {
        return albumId;
    }

    public int getTrackNumber() {
        return trackNumber;
    }

    public String getGenre() {
        return genre;
    }

    public void setGenre(String genre) {
        this.genre = genre;
    }

    public long getDuration() {
        return duration;
    }

    public void setDuration(long duration) {
        this.duration = duration;
    }
}
app/src/main/java/com/andryr/musicplayer/adapters/BaseAdapter.java
public abstract class BaseAdapter<VH extends RecyclerView.ViewHolder> extends RecyclerView.Adapter<VH> {

    private OnItemClickListener mOnItemClickListener;

    public void setOnItemClickListener(OnItemClickListener listener)
    {
        mOnItemClickListener = listener;
    }

    protected void triggerOnItemClickListener(int position, View view)
    {
        if(mOnItemClickListener != null)
        {
            mOnItemClickListener.onItemClick(position, view);
        }
    }

    public interface OnItemClickListener {
        void onItemClick(int position, View view);
    }
}
app/src/main/java/com/andryr/musicplayer/widgets/FastScroller.java
public class FastScroller extends View {

    private float mHandleY;


    private boolean mScrolling = false;


    private boolean mShowScroller = true;

    private RecyclerView mRecyclerView;

    private SectionIndexer mSectionIndexer;

    private ValueAnimator mBubbleAnimator = null;

    private Rect mBubbleTextBounds = new Rect();

    private String mBubbleText;

    private float mScrollerAlpha = 0.0F;
    private float mBubbleAlpha = 0.0F;



    private boolean mScrollerVisible = false;


    private Paint mPaint;
    private int mScrollerColor;

    private int mScrollerBackground;
    private float mHandleWidth;
    private float mHandleHeight;
    private float mBubbleTextSize;


    private boolean mShowBubble = true;
    private float mBubbleRadius;
    private Path mBubblePath = new Path();
    private RectF mBubbleRect = new RectF();
    private boolean mBubbleVisible = false;
    private ValueAnimator mScrollerAnimator = null;

    private ValueAnimator.AnimatorUpdateListener mHandleAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mScrollerAlpha = (float) animation.getAnimatedValue();
            invalidate();
        }
    };

    private ValueAnimator.AnimatorUpdateListener mBubbleAnimatorListener = new ValueAnimator.AnimatorUpdateListener() {
        @Override
        public void onAnimationUpdate(ValueAnimator animation) {
            mBubbleAlpha = (float) animation.getAnimatedValue();
            invalidate();
        }
    };

    private Runnable mHideScrollerRunnable = new Runnable() {

        @Override
        public void run() {
            hideScroller();

        }
    };

    private OnScrollListener mOnScrollListener = new OnScrollListener() {

        @Override
        public void onScrollStateChanged(RecyclerView recyclerView, int newState) {
            int visibleItems = recyclerView.getChildCount();
            int itemCount = recyclerView.getAdapter().getItemCount();

            if (((float) itemCount) / visibleItems < 2.0F) {
                mShowScroller = false;
                return;
            }
            if (newState == RecyclerView.SCROLL_STATE_IDLE && !mScrolling) {
                postDelayed(mHideScrollerRunnable, 1500);
            } else if (newState == RecyclerView.SCROLL_STATE_DRAGGING) {
                removeCallbacks(mHideScrollerRunnable);
                if (!mScrollerVisible) {
                    showScroller();
                }
            }

        }

        @Override
        public void onScrolled(RecyclerView recyclerView, int dx, int dy) {

            if (mScrolling || !mShowScroller) {
                return;
            }


            int extent = recyclerView.computeVerticalScrollExtent();


            int offset = recyclerView.computeVerticalScrollOffset();
            int range = recyclerView.computeVerticalScrollRange() - extent;


            float proportion = ((float) offset) / range;

            moveHandleTo(proportion);


        }

    };

    public FastScroller(Context context) {
        super(context);
        init(context, null);
    }

    public FastScroller(Context context, AttributeSet attrs) {
        super(context, attrs);
        init(context, attrs);
    }

    public FastScroller(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        init(context, attrs);
    }

    private void init(Context context, AttributeSet attrs) {
        if (attrs != null) {
            TypedArray a = context.getTheme().obtainStyledAttributes(attrs, R.styleable.FastScroller, R.attr.fastScrollerStyle, R.style.DefFastScrollerStyle);
            try {
                mHandleWidth = a.getDimension(R.styleable.FastScroller_handleWidth, 0);
                mHandleHeight = a.getDimension(R.styleable.FastScroller_handleHeight, 0);
                mScrollerColor = a.getColor(R.styleable.FastScroller_scrollerColor, 0);
                mBubbleTextSize = a.getDimension(R.styleable.FastScroller_bubbleTextSize, 0);
                mBubbleRadius = a.getDimension(R.styleable.FastScroller_bubbleRadius, 0);
            } finally {
                a.recycle();
            }
        }
        mScrollerBackground = context.getResources().getColor(R.color.fast_scroller_background);



        mPaint = new Paint();
        mPaint.setAntiAlias(true);
        mPaint.setTextSize(mBubbleTextSize);


    }

    public void setRecyclerView(RecyclerView view) {
        mRecyclerView = view;
        mRecyclerView.setOnScrollListener(mOnScrollListener);

    }

    public void setShowBubble(boolean show) {
        this.mShowBubble = show;
    }

    public void setSectionIndexer(SectionIndexer si) {
        mSectionIndexer = si;
    }



    private void moveHandleTo(float proportion) {
        int height = getHeight();
        float pos = proportion * (height - mHandleHeight);

        mHandleY = pos;

        invalidate();

    }


    private void scrollTo(float pos) {
        float proportion = Math.max(0, pos / getHeight());
        int itemCount = mRecyclerView.getAdapter().getItemCount();
        int itemPos = Math.min((int) (proportion * itemCount), itemCount - 1);


        mRecyclerView.scrollToPosition(itemPos);

        float scrollerPos = pos - (mHandleHeight / 2);
        int height = getHeight();
        scrollerPos = Math.max(0,
                Math.min(height - mHandleHeight, scrollerPos));
        mHandleY = scrollerPos;

        updateBubble(itemPos);

        invalidate();

    }

    private void updateBubble(int position) {
        if (mSectionIndexer == null) {
            return;
        }
        mBubbleVisible = true;

        mBubbleText = mSectionIndexer.getSectionForPosition(position);
        mPaint.getTextBounds(mBubbleText, 0, mBubbleText.length(), mBubbleTextBounds);

    }


    /*@Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        if (ev.getAction() == MotionEvent.ACTION_DOWN) {
            float x = ev.getX();
            float y = ev.getY();

            float width = getWidth();
            if (x > width - mHandleWidth
                    && y > mHandleY && y < mHandleY + mHandleHeight) {
                mScrolling = true;
                removeCallbacks(mHideScrollerRunnable);
                if (!mScrollerVisible) {
                    showScroller();
                }

                return true;
            }
        }
        return false;
    }*/

    @Override
    public boolean onTouchEvent(MotionEvent ev) {
        float x = ev.getX();
        float y = ev.getY();

        switch (ev.getAction()) {
            case MotionEvent.ACTION_DOWN:
                float width = getWidth();
                float touchHandleWidth = mHandleWidth*3; // the touchable area is three times wider than the handle
                if (x > width - touchHandleWidth
                        && y > mHandleY && y < mHandleY + mHandleHeight) {
                    mScrolling = true;
                    removeCallbacks(mHideScrollerRunnable);
                    if (!mScrollerVisible) {
                        showScroller();
                    }
                    showBubble();
                    return true;
                }
                break;
            case MotionEvent.ACTION_MOVE:
                scrollTo(y);
                break;

            case MotionEvent.ACTION_CANCEL:
            case MotionEvent.ACTION_UP:
                scrollTo(y);
                hideBubble();
                mScrolling = false;
                mOnScrollListener.onScrollStateChanged(mRecyclerView,
                        RecyclerView.SCROLL_STATE_IDLE);

                break;
        }
        return mScrolling;
    }


    private void showScroller() {
        mScrollerAlpha = 1.0F;
        mScrollerVisible = true;
        invalidate();
    }


    private void showBubble() {
        mBubbleAlpha = 1.0F;
        mBubbleVisible = true;
        invalidate();
    }

    private void hideBubble() {

        if (mBubbleAnimator == null) {
            mBubbleAnimator = ValueAnimator.ofFloat(1.0F, 0.0F);
            mBubbleAnimator.addUpdateListener(mBubbleAnimatorListener);
            mBubbleAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mBubbleVisible = false;
                    invalidate();
                }
            });
        } else {
            mBubbleAnimator.cancel();
        }


        mBubbleAnimator.start();
    }

    private void hideScroller() {

        if (mScrollerAnimator == null) {
            mScrollerAnimator = ValueAnimator.ofFloat(1.0F, 0.0F);
            mScrollerAnimator.addUpdateListener(mHandleAnimatorListener);
            mScrollerAnimator.addListener(new AnimatorListenerAdapter() {
                @Override
                public void onAnimationEnd(Animator animation) {
                    mScrollerVisible = false;
                    invalidate();
                }
            });
        } else {
            mScrollerAnimator.cancel();
        }


        mScrollerAnimator.start();


    }


    @Override
    protected void onDraw(Canvas canvas) {
        super.onDraw(canvas);
        if (mScrollerVisible) {
            int width = getWidth();
            int height = getHeight();


            float scrollerX = width - mHandleWidth;
            mPaint.setColor(ColorUtils.applyAlpha(mScrollerBackground, mScrollerAlpha));
            canvas.drawRect(scrollerX, 0, width, height, mPaint);

            mPaint.setColor(ColorUtils.applyAlpha(mScrollerColor, mScrollerAlpha));
            canvas.drawRect(scrollerX, mHandleY, width, mHandleY + mHandleHeight, mPaint);

            if (mShowBubble && mBubbleVisible && mBubbleText != null) {
                mBubblePath.reset();
                mPaint.setColor(ColorUtils.applyAlpha(mScrollerColor, mBubbleAlpha));
                float cx = scrollerX - mBubbleRadius - getPaddingRight();
                float cy = Utils.getValueInRange(mHandleY + mHandleHeight / 2.0F - mBubbleRadius, getPaddingTop() + mBubbleRadius, height - getPaddingBottom() - mBubbleRadius);

                mBubbleRect.set(cx - mBubbleRadius, cy - mBubbleRadius, cx + mBubbleRadius, cy + mBubbleRadius);
                mBubblePath.addRoundRect(mBubbleRect, new float[]{mBubbleRadius, mBubbleRadius, mBubbleRadius, mBubbleRadius, 0, 0, mBubbleRadius, mBubbleRadius}, Path.Direction.CW);

                canvas.drawPath(mBubblePath,mPaint);

                mPaint.setColor(Color.WHITE);


                canvas.drawText(mBubbleText, cx - mBubbleTextBounds.width() / 2.0F, cy + mBubbleTextBounds.height() / 2.0F, mPaint);
            }


        }
    }

    public interface SectionIndexer {
        String getSectionForPosition(int position);
    }

}
app/src/main/java/com/andryr/musicplayer/utils/Playlists.java
public class Playlists {

    public static Uri createPlaylist(ContentResolver resolver, String playlistName) {
        Uri uri = MediaStore.Audio.Playlists.EXTERNAL_CONTENT_URI;
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Playlists.NAME, playlistName);
        return resolver.insert(uri, values);
    }

    private static int getSongCount(ContentResolver resolver, Uri uri) {
        String[] cols = new String[]{"count(*)"};

        Cursor cur = resolver.query(uri, cols, null, null, null);
        cur.moveToFirst();
        final int count = cur.getInt(0);
        cur.close();
        return count;
    }

    private static void insert(ContentResolver resolver, Uri uri,
                               long songId, int index) {

        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER,
                Integer.valueOf(index));
        values.put(MediaStore.Audio.Playlists.Members.AUDIO_ID, songId);
        resolver.insert(uri, values);

    }

    public static void addSongToPlaylist(ContentResolver resolver, long playlistId,
                                         long songId) {
        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
                playlistId);
        final int base = getSongCount(resolver, uri);
        insert(resolver, uri, songId, base + 1);
    }

    public static void addAlbumToPlaylist(ContentResolver resolver, long playlistId,
                                          long albumId) {
        String cols[] = {MediaStore.Audio.Media._ID};

        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
                playlistId);
        int index = getSongCount(resolver, uri) + 1;

        Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;

        Cursor cursor = resolver.query(musicUri, cols,
                MediaStore.Audio.Media.ALBUM_ID + " = " + albumId, null, null);

        if (cursor != null && cursor.moveToFirst()) {


            do {
                int songId = cursor.getInt(0);
                insert(resolver, uri, songId, index);
                index++;
            } while (cursor.moveToNext());


        }

        if (cursor != null) {
            cursor.close();
        }
    }

    public static void removeFromPlaylist(ContentResolver resolver,
                                          long playlistId, long audioId) {
        Uri uri = MediaStore.Audio.Playlists.Members.getContentUri("external",
                playlistId);

        resolver.delete(uri, MediaStore.Audio.Playlists.Members.AUDIO_ID
                + " = " + audioId, null);
    }

    public static boolean moveItem(ContentResolver res, long playlistId,
                                   int from, int to) {
        Uri uri = MediaStore.Audio.Playlists.Members
                .getContentUri("external", playlistId).buildUpon()
                .appendEncodedPath(String.valueOf(from))
                .appendQueryParameter("move", "true").build();
        ContentValues values = new ContentValues();
        values.put(MediaStore.Audio.Playlists.Members.PLAY_ORDER, to);
        return res.update(uri, values, null, null) != 0;
    }

}
app/src/main/java/com/andryr/musicplayer/adapters/SongListAdapter.java
public class SongListAdapter extends AdapterWithHeader<SongListAdapter.SongViewHolder> implements FastScroller.SectionIndexer {

    private final int mThumbWidth;
    private final int mThumbHeight;
    private final Context mContext;
    private List<Song> mSongList = Collections.emptyList();


    public SongListAdapter(Context c) {
        mThumbWidth = c.getResources().getDimensionPixelSize(R.dimen.art_thumbnail_size);
        mThumbHeight = mThumbWidth;
        mContext = c;
    }

    public void setData(List<Song> data) {
        mSongList = data;
        notifyDataSetChanged();
    }

    public List<Song> getSongList() {
        return mSongList;
    }

    @Override
    public SongViewHolder onCreateViewHolderImpl(ViewGroup parent, int viewType) {
        View itemView = LayoutInflater.from(parent.getContext()).inflate(
                R.layout.song_list_item, parent, false);

        return new SongViewHolder(itemView);
    }

    @Override
    public void onBindViewHolderImpl(SongViewHolder holder, int position) {
        Song song = getItem(position);

        holder.vTitle.setText(song.getTitle());
        holder.vArtist.setText(song.getArtist());

        //évite de charger des images dans les mauvaises vues si elles sont recyclées
        holder.vArtwork.setTag(position);

        ArtworkCache.getInstance().loadBitmap(song.getAlbumId(), holder.vArtwork, mThumbWidth, mThumbHeight, ArtworkHelper.getDefaultThumbDrawable(mContext));
    }

    public Song getItem(int position) {
        return mSongList.get(position);
    }

    @Override
    public int getItemCountImpl() {
        return mSongList.size();
    }

    @Override
    public int getItemViewTypeImpl(int position) {
        return 0;
    }

    @Override
    public String getSectionForPosition(int position) {
        if(position >= 1) { // on ne prend pas en compte le header
            position--; // je répète : on ne prend pas en compte le header
            String title = getItem(position).getTitle();
            if (title.length() > 0) {
                return title.substring(0, 1);
            }
        }
        return "";
    }

    public class SongViewHolder extends RecyclerView.ViewHolder implements View.OnClickListener {

        private final TextView vTitle;
        private final TextView vArtist;
        private final ImageView vArtwork;


        public SongViewHolder(View itemView) {
            super(itemView);
            vTitle = (TextView) itemView.findViewById(R.id.title);
            vArtist = (TextView) itemView.findViewById(R.id.artist);
            vArtwork = (ImageView) itemView.findViewById(R.id.artwork);
            itemView.setOnClickListener(this);

            ImageButton menuButton = (ImageButton) itemView.findViewById(R.id.menu_button);
            menuButton.setOnClickListener(this);


            Drawable drawable = menuButton.getDrawable();

            drawable.mutate();
            ThemeHelper.tintDrawable(itemView.getContext(), drawable);

        }

        @Override
        public void onClick(View v) {
            int position = getAdapterPosition();
            triggerOnItemClickListener(position, v);


        }
    }
}
app/src/main/java/com/andryr/musicplayer/utils/ThemeHelper.java
public class ThemeHelper {

    public static boolean isDarkThemeSelected(Context context) {

        String lightStr = context.getResources().getString(R.string.light);
        String darkStr = context.getResources().getString(R.string.dark);
        SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
        String prefKey = context.getString(R.string.pref_theme_base_key);
        return prefs.getString(prefKey, lightStr).equals(darkStr);
    }

    public static int getStyleColor(Context context, int attr) {
        int[] attrs = {attr};

        TypedArray ta = context.getTheme().obtainStyledAttributes(attrs);


        return ta.getColor(0, Color.BLACK);
    }

    public static void tintDrawable(Context context, Drawable drawable) {

        if (drawable != null) {
            drawable.mutate().setColorFilter(getStyleColor(context, android.R.attr.textColorPrimary), PorterDuff.Mode.SRC_ATOP);
        }
    }

    public static void tintImageView(Context context, ImageView imageView) {
        if (imageView != null) {
            imageView.setColorFilter(getStyleColor(context, android.R.attr.textColorPrimary), PorterDuff.Mode.SRC_ATOP);
        }
    }

    public static void tintCompoundDrawables(Context context, TextView textView) {
        for (Drawable d : textView.getCompoundDrawables()) {
            tintDrawable(context, d);
        }
    }


    public static int getResourcesColor(Context context, int id) {
        return context.getResources().getColor(id);
    }
}
app/src/main/java/com/andryr/musicplayer/loaders/SortOrder.java
public final class SortOrder {

    /**
     * This class is never instantiated
     */
    public SortOrder() {
    }

    /**
     * Artist sort order entries.
     */
    public interface ArtistSortOrder {
        /* Artist sort order A-Z */
        String ARTIST_A_Z = MediaStore.Audio.Artists.DEFAULT_SORT_ORDER;

        /* Artist sort order Z-A */
        String ARTIST_Z_A = ARTIST_A_Z + " DESC";

        /* Artist sort order number of songs */
        String ARTIST_NUMBER_OF_SONGS = MediaStore.Audio.Artists.NUMBER_OF_TRACKS
                + " DESC";

        /* Artist sort order number of albums */
        String ARTIST_NUMBER_OF_ALBUMS = MediaStore.Audio.Artists.NUMBER_OF_ALBUMS
                + " DESC";
    }

    /**
     * Album sort order entries.
     */
    public interface AlbumSortOrder {
        /* Album sort order A-Z */
        String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;

        /* Album sort order Z-A */
        String ALBUM_Z_A = ALBUM_A_Z + " DESC";

        /* Album sort order songs */
        String ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Albums.NUMBER_OF_SONGS
                + " DESC";

        /* Album sort order artist */
        String ALBUM_ARTIST = MediaStore.Audio.Albums.ARTIST;

        /* Album sort order year */
        String ALBUM_YEAR = MediaStore.Audio.Albums.FIRST_YEAR + " DESC";

    }

    /**
     * Song sort order entries.
     */
    public interface SongSortOrder {
        /* Song sort order A-Z */
        String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

        /* Song sort order Z-A */
        String SONG_Z_A = SONG_A_Z + " DESC";

        /* Song sort order artist */
        String SONG_ARTIST = MediaStore.Audio.Media.ARTIST;

        /* Song sort order album */
        String SONG_ALBUM = MediaStore.Audio.Media.ALBUM;

        /* Song sort order year */
        String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC";

        /* Song sort order duration */
        String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC";

        /* Song sort order date */
        String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC";
    }

    /**
     * Album song sort order entries.
     */
    public interface AlbumSongSortOrder {
        /* Album song sort order A-Z */
        String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

        /* Album song sort order Z-A */
        String SONG_Z_A = SONG_A_Z + " DESC";

        /* Album song sort order track list */
        String SONG_TRACK_LIST = MediaStore.Audio.Media.TRACK + ", "
                + MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

        /* Album song sort order duration */
        String SONG_DURATION = SongSortOrder.SONG_DURATION;
    }

    /**
     * Artist song sort order entries.
     */
    public interface ArtistSongSortOrder {
        /* Artist song sort order A-Z */
        String SONG_A_Z = MediaStore.Audio.Media.DEFAULT_SORT_ORDER;

        /* Artist song sort order Z-A */
        String SONG_Z_A = SONG_A_Z + " DESC";

        /* Artist song sort order album */
        String SONG_ALBUM = MediaStore.Audio.Media.ALBUM;

        /* Artist song sort order year */
        String SONG_YEAR = MediaStore.Audio.Media.YEAR + " DESC";

        /* Artist song sort order duration */
        String SONG_DURATION = MediaStore.Audio.Media.DURATION + " DESC";

        /* Artist song sort order date */
        String SONG_DATE = MediaStore.Audio.Media.DATE_ADDED + " DESC";
    }

    /**
     * Artist album sort order entries.
     */
    public interface ArtistAlbumSortOrder {
        /* Artist album sort order A-Z */
        String ALBUM_A_Z = MediaStore.Audio.Albums.DEFAULT_SORT_ORDER;

        /* Artist album sort order Z-A */
        String ALBUM_Z_A = ALBUM_A_Z + " DESC";

        /* Artist album sort order songs */
        String ALBUM_NUMBER_OF_SONGS = MediaStore.Audio.Artists.Albums.NUMBER_OF_SONGS
                + " DESC";

        /* Artist album sort order year */
        String ALBUM_YEAR = MediaStore.Audio.Artists.Albums.FIRST_YEAR
                + " DESC";
    }

}


Question: import android.app.Activity;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.Loader;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.PopupMenu;
import android.support.v7.widget.PopupMenu.OnMenuItemClickListener;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.andryr.musicplayer.MainActivity;
import com.andryr.musicplayer.R;
import com.andryr.musicplayer.adapters.AdapterWithHeader;
import com.andryr.musicplayer.adapters.BaseAdapter;
import com.andryr.musicplayer.adapters.SongListAdapter;
import com.andryr.musicplayer.fragments.dialog.ID3TagEditorDialog;
import com.andryr.musicplayer.fragments.dialog.PlaylistPicker;
import com.andryr.musicplayer.loaders.SongLoader;
import com.andryr.musicplayer.loaders.SortOrder;
import com.andryr.musicplayer.model.Playlist;
import com.andryr.musicplayer.model.Song;
import com.andryr.musicplayer.utils.Playlists;
import com.andryr.musicplayer.utils.PrefUtils;
import com.andryr.musicplayer.utils.RecyclerViewUtils;
import com.andryr.musicplayer.utils.ThemeHelper;
import com.andryr.musicplayer.widgets.FastScroller;
import java.util.List;
/*
 * Copyright 2016 andryr
 *
 * 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.
 */

package com.andryr.musicplayer.fragments;





public class SongListFragment extends BaseFragment {


    private static final String STATE_SHOW_TOOLBAR = "toolbar";
    private static final String STATE_SHOW_FASTSCROLLER = "fastscroller";


    private MainActivity mActivity;

    private RecyclerView mRecyclerView;
    private SongListAdapter mAdapter;

    private boolean mShowToolbar = false;
    private boolean mShowFastScroller = true;


    private LoaderManager.LoaderCallbacks<List<Song>> mLoaderCallbacks = new LoaderCallbacks<List<Song>>() {

        @Override
        public Loader<List<Song>> onCreateLoader(int id, Bundle args) {
            SongLoader loader = new SongLoader(getActivity());

            loader.setSortOrder(PrefUtils.getInstance().getSongSortOrder());
            return loader;
        }

        @Override
        public void onLoadFinished(Loader<List<Song>> loader, List<Song> songList) {
            populateAdapter(songList);

            PrefUtils prefUtils = PrefUtils.getInstance();
            String sortOrder = prefUtils.getSongSortOrder();

            mShowScrollerBubble = SortOrder.SongSortOrder.SONG_A_Z.equals(sortOrder) || SortOrder.SongSortOrder.SONG_Z_A.equals(sortOrder);

            if (mFastScroller != null) {
                mFastScroller.setShowBubble(mShowScrollerBubble);
            }
        }

        @Override
        public void onLoaderReset(Loader<List<Song>> loader) {
            // TODO Auto-generated method stub

        }
    };
    private boolean mShowScrollerBubble = true;
    private FastScroller mFastScroller;

    protected void populateAdapter(List<Song> songList) {
        mAdapter.setData(songList);
    }

Answer:     private ID3TagEditorDialog.OnTagsEditionSuccessListener mOnTagsEditionSuccessListener = new ID3TagEditorDialog.OnTagsEditionSuccessListener() {