part 1: load all device songs - music player in android
Create TabLayout with ViewPager and ask for permissions using Dexter Library
What we are going to do?
We will create a TabLayout with two items: one item for songs and another for albums and the TabLayout will be contacted with ViewPager. We will see implement of the album item's code in the next part.
In this article, we will load all songs which are available on the device and show them in RecyclerView.
Step 1: Create new project and setup resources
Open android studio and create a new android studio project with an empty activity.
I used the following colors for this app.
colors.xml
<color name="colorPrimary">#181A1C</color>
<color name="colorPrimaryDark">#151515</color>
<color name="colorAccent">#FFFFFF</color>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
Step 2: declaring permissions in the AndroidManifest.xml file:
In Android Marshmallow Runtime permissions are introduced, therefore, we need to add READ_EXTERNAL_STORAGE in our AndroidManifest.xml file to get songs from the user's device.
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
Step 3: working with activity_main.xml file:
copy the below code and replace it with the activity_main.xml's code. activity_main.xml<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/colorPrimary"
app:tabIndicatorColor="@color/white"
app:tabMode="fixed"
app:tabTextColor="@color/white" />
<androidx.viewpager.widget.ViewPager
android:id="@+id/viewPager"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Step 4: Create two Fragments:
We need to create two fragments: one for songs and another for albums. To do so, follow these steps.1: create the fragment_songs.xml file:
Create a new XML file with the name "fragment_songs.xml".
2: create the SongsFragment.java file:
Create a new Java class with the name "SongsFragment" and paste the below code.
import android.content.Context;
import android.view.View;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import androidx.fragment.app.Fragment;
public class SongsFragment extends Fragment{
Context context;
public SongsFragment(Context context){
this.context=context;
}
@Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
return super.onCreateView(arg0, arg1, arg2);
}
}
3: create the fragment_albums.xml file:
Create a new XML file with the name "fragment_albums.xml".
4: create the AlbumsFragment.java file:
Create a new Java class with the name " AlbumsFragment" and paste the below code.
import android.content.Context;
import android.view.View;
import android.os.Bundle;
import android.view.ViewGroup;
import android.view.LayoutInflater;
import androidx.fragment.app.Fragment;
public class AlbumsFragment extends Fragment{
Context context;
public AlbumsFragment(Context context){
this.context=context;
}
@Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
return super.onCreateView(arg0, arg1, arg2);
}
}
Step 5: add Dexter Library to the build.gradle file:
Open the build.gradle (Module level) file and add the following dependency in the dependencies section. Dexter library is used for asking permissions at runtime.
build.gradle > dependencies:
implementation 'com.karumi:dexter:6.2.3'
Note:After adding this dependency make sure to sync the project.
Step 6: create the CustomPagerAdapter.java file:
Create a new Java class with the name of"CustomPagerAdapter" and paste the below code:
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.Fragment;
import java.util.ArrayList;
import androidx.fragment.app.FragmentPagerAdapter;
import java.util.List;
public class CustomPagerAdapter extends FragmentPagerAdapter {
ArrayList<String> arrayListTitle = new ArrayList<>();
ArrayList<Fragment> list = new ArrayList<>();
FragmentManager fragmentManager;
public CustomPagerAdapter(ArrayList<Fragment> list, ArrayList<String> arrayListTitle,
FragmentManager fragmentManager) {
super(fragmentManager);
this.list = list;
this.fragmentManager = fragmentManager;
this.arrayListTitle = arrayListTitle;
}
@Override
public long getItemId(int arg0) {
return list.get(arg0).getId();
}
@Override
public int getCount() {
return list.size();
}
@Override
public Fragment getItem(int arg0) {
return list.get(arg0);
}
@Override
public CharSequence getPageTitle(int arg0) {
return arrayListTitle.get(arg0);
}
}
We created this class because we want to use our TabLayout with ViewPager.
Step 7: working with the MainActivity.java file:
now it's time to work with MainActivity.java! First, we need to implement the code for asking READ_EXTERNAL_STORAGE from the user at runtime. Then we need to set up the ViewPager with TabLayout. To achieve this, Refer to the below code.
import android.Manifest;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import android.os.Bundle;
import androidx.fragment.app.Fragment;
import com.karumi.dexter.Dexter;
import androidx.viewpager.widget.ViewPager;
import com.google.android.material.tabs.TabLayout;
import com.karumi.dexter.listener.single.PermissionListener;
import com.karumi.dexter.listener.PermissionGrantedResponse;
import com.karumi.dexter.PermissionToken;
import com.karumi.dexter.listener.PermissionDeniedResponse;
import com.karumi.dexter.listener.PermissionRequest;
import java.util.ArrayList;
public class MainActivity extends AppCompatActivity {
TabLayout tabLayout;
ViewPager pager;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
tabLayout = findViewById(R.id.tabLayout);
pager = findViewById(R.id.viewPager);
Dexter.withContext(this).withPermission(Manifest.permission.READ_EXTERNAL_STORAGE)
.withListener(new PermissionListener() {
@Override
public void onPermissionGranted(PermissionGrantedResponse response) {
setUpViewPager();
}
@Override
public void onPermissionDenied(PermissionDeniedResponse response) {
Toast.makeText(getApplicationContext(), "permission needed", Toast.LENGTH_SHORT).show();
}
@Override
public void onPermissionRationaleShouldBeShown(PermissionRequest permission,
PermissionToken token) {
token.continuePermissionRequest();
}
}).check();
}
public void setUpViewPager(){
ArrayList<String> arrayListTitles = new ArrayList<>();
ArrayList<Fragment> arrayListFragments = new ArrayList<>();
arrayListFragments.add(new SongsFragment(getApplicationContext()));
arrayListFragments.add(new AlbumsFragment(getApplicationContext()));
arrayListTitles.add("Songs");
arrayListTitles.add("Albums");
CustomPagerAdapter adapter = new CustomPagerAdapter(arrayListFragments, arrayListTitles,
getSupportFragmentManager());
pager.setAdapter(adapter);
tabLayout.setupWithViewPager(pager);
}
}
Now let's see if everything is going well!
Output:
Get all songs and show them in a RecyclerView
Now, we will see how to get all songs from the device/phone and show them in a RecyclerView.
For that, first, we have to add RecyclerView to the fragment_songs.xml file:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical" >
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/fragment_songs_recyclerview"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</LinearLayout>
Add Glide Library and Circular ImageView library to the build.gradle file:
//Glide library to load album arts of the songs in image views
implementation 'com.github.bumptech.glide:glide:4.13.0'
//circleimageview library to make imageviews circular
implementation 'de.hdodenhof:circleimageview:3.1.0'
Now to show the album art, title, duration, and size of every song, we have to create a custom XML file. For that create a new XML file with the name "item_song.xml" and paste the below code.
<LinearLayout 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:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<androidx.cardview.widget.CardView
android:id="@+id/item_song_cardview"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_margin="4dp"
android:layout_marginLeft="4dp"
android:layout_marginRight="4dp"
android:padding="8dp"
app:cardCornerRadius="20dp"
app:cardElevation="10dp" >
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal" >
<de.hdodenhof.circleimageview.CircleImageView
xmlns:app="http://schemas.android.com/apk/res-auto"
android:id="@+id/item_song_imageview"
android:layout_width="60dp"
android:layout_height="60dp"
android:layout_marginBottom="8dp"
android:layout_marginTop="8dp"
android:src="@drawable/music"
app:civ_border_color="@color/black"
app:civ_border_width="2dp" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical" >
<TextView
android:id="@+id/item_song_title"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginTop="12dp"
android:layout_marginLeft="5dp"
android:ellipsize="marquee"
android:marqueeRepeatLimit="marquee_forever"
android:singleLine="true"
android:text="Song name goes here"
android:textColor="@color/black"
android:textSize="18sp"
android:textStyle="bold" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal" >
<TextView
android:id="@+id/item_song_duration"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:singleLine="true"
android:text="03:56"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="12sp" />
<TextView
android:id="@+id/item_song_size"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginBottom="8dp"
android:layout_marginLeft="5dp"
android:layout_weight="1"
android:singleLine="true"
android:text="6.8 MB"
android:textAlignment="center"
android:textColor="@color/black"
android:textSize="12sp" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</androidx.cardview.widget.CardView>
</LinearLayout>
Now, create a new Java file "Song.java" to store the data of the song and paste the below code.
import java.util.Locale;
import java.util.concurrent.TimeUnit;
import androidx.annotation.NonNull;
public class Song {
private String mTitle; //To store the song title
private long mDuration; //To store the song duration in milliseconds
private String mPath; //To store the path of the song
private String mAlbumName; //To store the album name
private String mArtistName; //To store the artist name
private long mSize; //To store the size of the song in milliseconds
private int mAlbumId; //To store the id of the album
//method to return the song name
@NonNull
public final String getSongTitle() {
return mTitle;
}
//method to set the song name
@NonNull
public void setSongTitle(String title) {
mTitle = title;
}
//method to get song duration
public final long getSongDuration() {
return mDuration;
}
//method to set song duration
@NonNull
public void setSongDuration(long duration) {
mDuration = duration;
}
//method to get song path
@NonNull
public final String getSongPath() {
return mPath;
}
//method to set song path
@NonNull
public void setSongPath(String path) {
mPath = path;
}
//method to get album name
@NonNull
public final String getAlbumName() {
return mAlbumName;
}
//method to set album name
@NonNull
public void setAlbumName(String name) {
mAlbumName = name;
}
//method to get the artist's name
@NonNull
public final String getArtistName() {
return mArtistName;
}
//method to set artist name
@NonNull
public void setArtistName(String name) {
mArtistName = name;
}
//method to set song size
@NonNull
public void setSongSize(long number) {
mSize = number;
}
//method to get song size
public final long getSongSize() {
return mSize;
}
//method to set album id
@NonNull
public void setAlbumId(int id) {
mAlbumId = id;
}
//method to get album id
public final int getAlbumId() {
return mAlbumId;
}
}
After doing this, create a new java file with the name "Utils.java" and paste the below code.
This class contains some important methods which will be used in this tutorial.
import android.media.MediaMetadataRetriever;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.text.DecimalFormat;
import java.util.Locale;
public class Utils {
//method to format the size
public static String formatSize(long size) {
String hrSize = null;
double b = size;
double k = size / 1024.0;
double m = ((size / 1024.0) / 1024.0);
double g = (((size / 1024.0) / 1024.0) / 1024.0);
double t = ((((size / 1024.0) / 1024.0) / 1024.0) / 1024.0);
DecimalFormat dec = new DecimalFormat("0.00");
if (t > 1) {
hrSize = dec.format(t).concat(" TB");
} else if (g > 1) {
hrSize = dec.format(g).concat(" GB");
} else if (m > 1) {
hrSize = dec.format(m).concat(" MB");
} else if (k > 1) {
hrSize = dec.format(k).concat(" KB");
} else {
hrSize = dec.format(b).concat(" Bytes");
}
return hrSize;
}
//This method takes the duration of the song in milliseconds and returns the duration in minutes and seconds
public static String formatDuration(final long duration) {
return String.format(Locale.getDefault(), "%02d:%02d", TimeUnit.MILLISECONDS.toMinutes(duration),
TimeUnit.MILLISECONDS.toSeconds(duration)
- TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration)));
}
//method to get album art
public static byte[] getAlbumArt(String uri) {
MediaMetadataRetriever retriever = new MediaMetadataRetriever();
try {
retriever.setDataSource(uri);
} catch (Exception e) {
}
byte[] art = retriever.getEmbeddedPicture();
retriever.release();
return art;
}
}
Now create a new Java class that extends RecyclerView.Adapter<>(), name this class as "SongsFragmentAdapter". Which will contains the following code:
import android.content.Context;
import android.content.Intent;
import android.graphics.Color;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Filter;
import android.widget.Filterable;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import androidx.cardview.widget.CardView;
import androidx.palette.graphics.Palette;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import java.util.List;
import java.util.ArrayList;
public class SongsFragmentAdapter extends RecyclerView.Adapter<SongsFragmentAdapter.MyViewHolder>{
Context context;
ArrayList<Song> songArrayList=new ArrayList<>();
public class MyViewHolder extends RecyclerView.ViewHolder{
public TextView title,duration,size;
public ImageView albumArt;
CardView cardView;
public MyViewHolder(View v){
super(v);
title=v.findViewById(R.id.item_song_title);
size=v.findViewById(R.id.item_song_size);
duration=v.findViewById(R.id.item_song_duration);
albumArt=v.findViewById(R.id.item_song_imageview);
cardView=v.findViewById(R.id.item_song_cardview);
}
}
public SongsFragmentAdapter(Context context,ArrayList<Song> songArrayList){
this.context=context;
this.songArrayList=songArrayList;
}
@Override
public MyViewHolder onCreateViewHolder(ViewGroup arg0, int arg1) {
View v=LayoutInflater.from(context).inflate(R.layout.item_song,arg0,false);
return new MyViewHolder(v);
}
@Override
public void onBindViewHolder(MyViewHolder myViewHolder, int position) {
Song song=songArrayList.get(position);
myViewHolder.title.setText(song.getSongTitle());
Utils utils=new Utils();
myViewHolder.size.setText(Utils.formatSize(song.getSongSize()));
myViewHolder.duration.setText(Utils.formatDuration(song.getSongDuration()));
byte[] bitmap=utils.getAlbumArt(song.getSongPath());
if (bitmap != null){
Glide.with(context).asBitmap().load(bitmap).into(myViewHolder.albumArt);
}
else {
Glide.with(context).load(R.drawable.music).into(myViewHolder.albumArt);
}
}
@Override
public int getItemCount() {
return songArrayList.size();
}
}
And finally, paste the below code in the SongsFragment.java file:
import android.content.ContentResolver;
import android.content.Context;
import android.database.Cursor;
import android.net.Uri;
import android.provider.MediaStore;
import android.view.MenuInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.ViewGroup;
import android.view.View;
import android.view.LayoutInflater;
import android.os.Bundle;
import androidx.appcompat.widget.SearchView;
import androidx.fragment.app.Fragment;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import java.util.ArrayList;
public class SongsFragment extends Fragment {
Context context;
public RecyclerView recyclerview;
public static SongsFragmentAdapter songsFragmentAdapter;
public static ArrayList<Song> songArrayList = new ArrayList<>();
Cursor cursor;
ContentResolver contentResolver;
public SongsFragment(Context context) {
this.context = context;
}
@Override
public View onCreateView(LayoutInflater arg0, ViewGroup arg1, Bundle arg2) {
View view = arg0.inflate(R.layout.fragment_songs, arg1, false);
recyclerview = view.findViewById(R.id.fragment_songs_recyclerview);
contentResolver = context.getContentResolver();
Uri uri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
String[] projection = { MediaStore.Audio.Media.TITLE, MediaStore.Audio.Media.DATA,
MediaStore.Audio.Media.ARTIST, MediaStore.Audio.Media.DURATION, MediaStore.MediaColumns.SIZE,
MediaStore.Audio.Media.ALBUM_ID, MediaStore.Audio.Media.ALBUM };
String order = MediaStore.Audio.Media.TITLE;
Cursor cursor = contentResolver.query(uri, projection, null, null, order);
if (cursor != null) {
if (cursor.moveToFirst()) {
do {
String title = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.TITLE));
String path = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DATA));
String artist = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ARTIST));
long duration = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.DURATION));
long size = cursor.getLong(cursor.getColumnIndexOrThrow(MediaStore.MediaColumns.SIZE));
String album = cursor.getString(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM));
int albumId = cursor.getInt(cursor.getColumnIndexOrThrow(MediaStore.Audio.Media.ALBUM_ID));
Song song = new Song();
song.setSongTitle(title);
song.setSongPath(path);
song.setSongSize(size);
song.setSongDuration(duration);
song.setAlbumName(album);
song.setArtistName(artist);
song.setAlbumId(albumId);
songArrayList.add(song);
} while (cursor.moveToNext());
}
}
cursor.close();
songsFragmentAdapter = new SongsFragmentAdapter(context, songArrayList);
recyclerview.setAdapter(songsFragmentAdapter);
recyclerview.setLayoutManager(new LinearLayoutManager(context));
return view;
}
}
Now run the app!
In this part, we successfully created a TabLayout with ViewPager and displayed all device songs in a RecyclerView. In the next part, we will part we will get all song albums and show them in RecyclerView.If you want to help me, then share this article with others, so that they can also learn something new! and if you have any queries then comment down below!
Thanks for the reading!
Post a Comment