Sunday, November 17, 2024
Google search engine
HomeLanguagesJavaHow to Build Spotify Clone in Android?

How to Build Spotify Clone in Android?

There are many android applications present in the market which are available to play songs on mobile phones. The famous one of all these music players is Spotify. In this article, we will be building a Spotify clone application using Spotify Web APIs. A sample video is given below to get an idea about what we are going to do in this article. Note that we are going to implement this project using the Java language. 

Step by Step Implementation

Step 1: Create a New Project

To create a new project in Android Studio please refer to How to Create/Start a New Project in Android Studio. Note that select Java as the programming language.

Step 2: Add dependency to use volley for json parsing in android

Navigate to app>Gradle Scripts>build.gradle file and add the below dependency to it in the dependencies section.

implementation 'com.android.volley:volley:1.2.1'
implementation 'com.squareup.picasso:picasso:2.71828'

After adding the above dependency simply sync your project to install it. 

Step 3: Create a new application on Spotify Console

Navigate to Spotify Developer Console. Simply Login with your username and password. Now you will get to see an option to create a new app. Simply click on that option and then specify the app name and app description and create a new application. Now you will be navigated to the application inside which we will get to see the app client id and app client secret key.  We have to simply copy the client id and client secret key. We will be using these 2 parameters for generating our authorization. Now for generating authorization we have to paste the client id and client secret key in the below format.

client_id:client_secret_key

After adding it to this format. Simply copy the complete string and now we will be converting this string to base64. For converting it. We have to navigate to the below URL. On this website, we have to paste the above-copied client id and client secret key in the above format. Then convert it into base 64 to generate our authorization. Now we will be using this authorization to generate our access token which we will be using to access the data from Spotify. 

Note: You can also refer below documentation on How to generate authorization to use Spotify APIS.

Step 4: Updating theme and colors within our project

Navigate to app>res>values>colors.xml file and add the below colors to it. Below is the code for the colors.xml file. 

XML




<?xml version="1.0" encoding="utf-8"?>
<resources>
    <color name="purple_200">#141414</color>
    <color name="purple_500">#141414</color>
    <color name="purple_700">#141414</color>
    <color name="teal_200">#FF03DAC5</color>
    <color name="teal_700">#FF018786</color>
    <color name="black">#FF000000</color>
    <color name="white">#FFFFFFFF</color>
    <color name="back_color">#141414</color>
    <color name="fab_color">#20D761</color>
</resources>


Now for updating the theme of our project. Navigate to app>res>values>themes> Change theme from Dark Action Bar to No Action Bar. Below is the code for the themes.xml file. 

XML




<resources xmlns:tools="http://schemas.android.com/tools">
    <!-- Base application theme. -->
    <style name="Theme.SpotifyJava" parent="Theme.MaterialComponents.DayNight.NoActionBar">
        <!-- Primary brand color. -->
        <item name="colorPrimary">@color/purple_500</item>
        <item name="colorPrimaryVariant">@color/purple_700</item>
        <item name="colorOnPrimary">@color/white</item>
        <!-- Secondary brand color. -->
        <item name="colorSecondary">@color/teal_200</item>
        <item name="colorSecondaryVariant">@color/teal_700</item>
        <item name="colorOnSecondary">@color/black</item>
        <!-- Status bar color. -->
        <item name="android:statusBarColor" tools:targetApi="l">?attr/colorPrimaryVariant</item>
        <!-- Customize your theme here. -->
    </style>
</resources>


Step 5: Create a new layout file for the Album item to be displayed in RecyclerView

Navigate to app>res>layout> Right-click on it>New Layout Resource file. Name it album_rv_item and add the below code to it. Comments are added in the code to get to know it in detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_centerInParent="true"
    android:layout_gravity="center"
    android:layout_margin="4dp"
    android:background="@color/back_color"
    android:padding="4dp">
   
    <!--image view for displaying album image-->
    <ImageView
        android:id="@+id/idIVAlbum"
        android:layout_width="120dp"
        android:layout_height="120dp" />
 
    <!-- text view for displaying album name -->
    <TextView
        android:id="@+id/idTVAlbumName"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:layout_below="@id/idIVAlbum"
        android:layout_margin="3dp"
        android:lines="1"
        android:maxLines="1"
        android:padding="4dp"
        android:text="Album Name"
        android:textColor="@color/white"
        android:textSize="12sp"
        android:textStyle="bold" />
 
    <!-- text view for displaying album artist-->
    <TextView
        android:id="@+id/idTVALbumDetails"
        android:layout_width="120dp"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVAlbumName"
        android:layout_marginStart="3dp"
        android:layout_marginTop="-5dp"
        android:layout_marginEnd="3dp"
        android:lines="1"
        android:maxLines="1"
        android:padding="4dp"
        android:text="Album Details"
        android:textColor="@color/white"
        android:textSize="12sp" />
 
</RelativeLayout>


Step 6: Working with the activity_main.xml file

Navigate to app>res>layout>activity_main.xml file and add the below code to it. Comments are added in the code to get to know it in detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<ScrollView
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/back_color"
    tools:context=".MainActivity">
 
    <RelativeLayout
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:background="@color/back_color">
 
        <!-- text view to display greeting-->
        <TextView
            android:id="@+id/idTVGreetHeading"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_margin="10dp"
            android:padding="10dp"
            android:text="Good afternoon"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:textStyle="bold" />
 
        <!-- text view to display search heading-->
        <TextView
            android:id="@+id/idTVSearchHeading"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idTVGreetHeading"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:padding="10dp"
            android:text="Search"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:textStyle="bold" />
 
        <!-- edit text  to search songs-->
        <EditText
            android:id="@+id/idEdtSearch"
            android:layout_width="match_parent"
            android:layout_height="50dp"
            android:layout_below="@id/idTVSearchHeading"
            android:layout_marginStart="10dp"
            android:layout_marginTop="5dp"
            android:layout_marginEnd="10dp"
            android:layout_marginBottom="5dp"
            android:background="@color/white"
            android:hint="Artists,songs or podcasts"
            android:imeOptions="actionDone"
            android:lines="1"
            android:padding="10dp"
            android:singleLine="true"
            android:textColorHint="@android:color/darker_gray"
            android:textStyle="bold" />
 
        <!-- text view to display heading-->
        <TextView
            android:id="@+id/idTVHeading1"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idEdtSearch"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:padding="10dp"
            android:text="Recommended for Today"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:textStyle="bold" />
 
        <!-- recycler view for various albums-->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/idRVAlbums"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idTVHeading1"
            android:layout_margin="4dp"
            android:orientation="horizontal"
            android:padding="4dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/album_rv_item" />
 
        <!-- text view to display heading-->
        <TextView
            android:id="@+id/idTVHeading2"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idRVAlbums"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:padding="10dp"
            android:text="Popular Albums"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:textStyle="bold" />
 
        <!-- recycler view for popular albums-->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/idRVPopularAlbums"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idTVHeading2"
            android:layout_margin="4dp"
            android:orientation="horizontal"
            android:padding="4dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/album_rv_item" />
 
        <!-- text view to display heading-->
        <TextView
            android:id="@+id/idTVHeading3"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idRVPopularAlbums"
            android:layout_marginStart="10dp"
            android:layout_marginEnd="10dp"
            android:padding="10dp"
            android:text="Trending now"
            android:textColor="@color/white"
            android:textSize="20sp"
            android:textStyle="bold" />
 
        <!-- recycler view for various albums-->
        <androidx.recyclerview.widget.RecyclerView
            android:id="@+id/idRVTrendingAlbums"
            android:layout_width="match_parent"
            android:layout_height="wrap_content"
            android:layout_below="@id/idTVHeading3"
            android:layout_margin="4dp"
            android:orientation="horizontal"
            android:padding="4dp"
            app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
            tools:listitem="@layout/album_rv_item" />
 
    </RelativeLayout>
   
</ScrollView>


Step 7: Creating a Modal class for Album Details

Navigate to app>java>your app’s package name>Right click on it>New Java class and name it as AlbumRVModal and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
public class AlbumRVModal {
    // o below line creating variables
    // for different parameters.
    public String album_type;
    public String artistName;
    public String external_ids;
    public String external_urls;
    public String href;
    public String id;
    public String imageUrl;
    public String label;
    public String name;
    public int popularity;
    public String release_date;
    public int total_tracks;
    public String type;
 
    // creating constructor on below line.
    public AlbumRVModal(String album_type, String artistName, String external_ids, String external_urls, String href, String id, String imageUrl, String label, String name, int popularity, String release_date, int total_tracks, String type) {
        this.album_type = album_type;
        this.artistName = artistName;
        this.external_ids = external_ids;
        this.external_urls = external_urls;
        this.href = href;
        this.id = id;
        this.imageUrl = imageUrl;
        this.label = label;
        this.name = name;
        this.popularity = popularity;
        this.release_date = release_date;
        this.total_tracks = total_tracks;
        this.type = type;
    }
}


Step 8: Create a new activity to display songs in the album

Navigate to app>java>your app’s package name> Right-click on it>New Activity>Empty Activity and name it as AlbumDetailActivity. 

Step 9: Create an adapter class for the albums RecyclerView

Navigate to app>java>your app’s package name>Right click on it>New Java class and name it as AlbumRVAdapter and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ImageView;
import android.widget.TextView;
 
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
 
import com.squareup.picasso.Picasso;
 
import java.util.ArrayList;
 
public class AlbumRVAdapter extends RecyclerView.Adapter<AlbumRVAdapter.ViewHolder> {
    // creating variables for array list and context
    private ArrayList<AlbumRVModal> albumRVModalArrayList;
    private Context context;
 
    // creating a constructor.
    public AlbumRVAdapter(ArrayList<AlbumRVModal> albumRVModalArrayList, Context context) {
        this.albumRVModalArrayList = albumRVModalArrayList;
        this.context = context;
    }
 
    @NonNull
    @Override
    public AlbumRVAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // inflating layout file on below line.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.album_rv_item, parent, false);
        return new ViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(@NonNull AlbumRVAdapter.ViewHolder holder, int position) {
        // setting data to text view and image view on below line.
        AlbumRVModal albumRVModal = albumRVModalArrayList.get(position);
        Picasso.get().load(albumRVModal.imageUrl).into(holder.albumIV);
        holder.albumNameTV.setText(albumRVModal.name);
        holder.albumDetailTV.setText(albumRVModal.artistName);
        // adding click listener for album item on below line.
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // on below line opening a new album detail
                // activity for displaying songs within that album.
                Intent i = new Intent(context, AlbumDetailActivity.class);
                // on below line passing album related data.
                i.putExtra("id", albumRVModal.id);
                i.putExtra("name", albumRVModal.name);
                i.putExtra("img", albumRVModal.imageUrl);
                i.putExtra("artist", albumRVModal.artistName);
                i.putExtra("albumUrl", albumRVModal.external_urls);
                context.startActivity(i);
            }
        });
    }
 
    // on below line returning the size of list
    @Override
    public int getItemCount() {
        return albumRVModalArrayList.size();
    }
 
    public class ViewHolder extends RecyclerView.ViewHolder {
 
        // on below line creating variables
        // for image view and text view.
        private ImageView albumIV;
        private TextView albumNameTV, albumDetailTV;
 
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            // on below line initializing variables.
            albumIV = itemView.findViewById(R.id.idIVAlbum);
            albumNameTV = itemView.findViewById(R.id.idTVAlbumName);
            albumDetailTV = itemView.findViewById(R.id.idTVALbumDetails);
        }
    }
}


Step 10: Create a new activity that will allow us to search various tracks

Navigate to app>java>your app’s package name>Right click on it>New Empty Activity and name it as SearchActivity. 

Step 11: Working with the MainActivity.java file

Navigate to app>java>your app’s package name>MainActivity.java file and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
 
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
 
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.StringRequest;
import com.android.volley.toolbox.Volley;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
public class MainActivity extends AppCompatActivity {
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);
        // on below line calling method to
        // load our recycler views and search view.
        initializeAlbumsRV();
        initializePopularAlbumsRV();
        initializeTrendingAlbumsRV();
        initializeSearchView();
    }
 
    // below method is use to initialize search view.
    private void initializeSearchView() {
        // on below line initializing our
        // edit text for search views.
        EditText searchEdt = findViewById(R.id.idEdtSearch);
        searchEdt.setOnEditorActionListener(new EditText.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    // on below line calling method to search tracks.
                    searchTracks(searchEdt.getText().toString());
                    return true;
                }
                return false;
            }
        });
    }
 
    // below method is use to open search
    // tracks activity to search tracks.
    private void searchTracks(String searchQuery) {
        // on below line opening search activity to
        // display search results in search activity.
        Intent i = new Intent(MainActivity.this, SearchActivity.class);
        i.putExtra("searchQuery", searchQuery);
        startActivity(i);
    }
 
    // below method is use to get token.
    private String getToken() {
        // on below line getting token from shared prefs.
        SharedPreferences sh = getSharedPreferences("MySharedPref", MODE_PRIVATE);
        return sh.getString("token", "Not Found");
    }
 
    @Override
    protected void onStart() {
        super.onStart();
        // on below line calling generate
        // token method to generate token.
        generateToken();
    }
 
    private void generateToken() {
        // on below line creating a variable for
        // url to generate access token
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // on below line making string request to generate access token.
        StringRequest request = new StringRequest(Request.Method.POST, url, new com.android.volley.Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                try {
                    JSONObject jsonObject = new JSONObject(response);
                    // on below line getting access token and
                    // saving it to shared preferences.
                    String tk = jsonObject.getString("access_token");
                    SharedPreferences sharedPreferences = getSharedPreferences("MySharedPref", MODE_PRIVATE);
                    SharedPreferences.Editor myEdit = sharedPreferences.edit();
                    myEdit.putString("token", "Bearer " + tk);
                    myEdit.apply();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
 
            }
        }, new com.android.volley.Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                // method to handle errors.
                Toast.makeText(MainActivity.this, "Fail to get response = " + error, Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                HashMap<String, String> headers = new HashMap<>();
                // on below line passing headers.
                // Make sure to add your authorization.
                headers.put("Authorization", " Add your authorization here.");
                headers.put("Content-Type", "application/x-www-form-urlencoded");
                return headers;
            }
        };
        // adding request to queue.
        queue.add(request);
    }
 
 
    private void initializeAlbumsRV() {
        // on below line initializing albums rv
        RecyclerView albumsRV = findViewById(R.id.idRVAlbums);
        // on below line creating list, initializing adapter
        // and setting it to recycler view.
        ArrayList<AlbumRVModal> albumRVModalArrayList = new ArrayList<>();
        AlbumRVAdapter albumRVAdapter = new AlbumRVAdapter(albumRVModalArrayList, this);
        albumsRV.setAdapter(albumRVAdapter);
        // on below line creating a variable for url
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // on below line making json object request to parse json data.
        JsonObjectRequest albumObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONArray albumArray = response.getJSONArray("albums");
                    for (int i = 0; i < albumArray.length(); i++) {
                        JSONObject albumObj = albumArray.getJSONObject(i);
                        String album_type = albumObj.getString("album_type");
                        String artistName = albumObj.getJSONArray("artists").getJSONObject(0).getString("name");
                        String external_ids = albumObj.getJSONObject("external_ids").getString("upc");
                        String external_urls = albumObj.getJSONObject("external_urls").getString("spotify");
                        String href = albumObj.getString("href");
                        String id = albumObj.getString("id");
                        String imgUrl = albumObj.getJSONArray("images").getJSONObject(1).getString("url");
                        String label = albumObj.getString("label");
                        String name = albumObj.getString("name");
                        int popularity = albumObj.getInt("popularity");
                        String release_date = albumObj.getString("release_date");
                        int total_tracks = albumObj.getInt("total_tracks");
                        String type = albumObj.getString("type");
                        // on below line adding data to array list.
                        albumRVModalArrayList.add(new AlbumRVModal(album_type, artistName, external_ids, external_urls, href, id, imgUrl, label, name, popularity, release_date, total_tracks, type));
                    }
                    albumRVAdapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get data : " + error, Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                // on below line passing headers.
                HashMap<String, String> headers = new HashMap<>();
                headers.put("Authorization", getToken());
                headers.put("Accept", "application/json");
                headers.put("Content-Type", "application/json");
                return headers;
            }
        };
        // on below line adding request to queue.
        queue.add(albumObjReq);
    }
 
    private void initializePopularAlbumsRV() {
        // on below line creating list, initializing
        // adapter and setting it to recycler view.
        RecyclerView albumsRV = findViewById(R.id.idRVPopularAlbums);
        ArrayList<AlbumRVModal> albumRVModalArrayList = new ArrayList<>();
        AlbumRVAdapter albumRVAdapter = new AlbumRVAdapter(albumRVModalArrayList, this);
        albumsRV.setAdapter(albumRVAdapter);
        // on below line creating a variable for url
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // on below line making json object request to parse json data.
        JsonObjectRequest albumObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONArray albumArray = response.getJSONArray("albums");
                    for (int i = 0; i < albumArray.length(); i++) {
                        JSONObject albumObj = albumArray.getJSONObject(i);
                        String album_type = albumObj.getString("album_type");
                        String artistName = albumObj.getJSONArray("artists").getJSONObject(0).getString("name");
                        String external_ids = albumObj.getJSONObject("external_ids").getString("upc");
                        String external_urls = albumObj.getJSONObject("external_urls").getString("spotify");
                        String href = albumObj.getString("href");
                        String id = albumObj.getString("id");
                        String imgUrl = albumObj.getJSONArray("images").getJSONObject(1).getString("url");
                        String label = albumObj.getString("label");
                        String name = albumObj.getString("name");
                        int popularity = albumObj.getInt("popularity");
                        String release_date = albumObj.getString("release_date");
                        int total_tracks = albumObj.getInt("total_tracks");
                        String type = albumObj.getString("type");
                        // on below line adding data to array list.
                        albumRVModalArrayList.add(new AlbumRVModal(album_type, artistName, external_ids, external_urls, href, id, imgUrl, label, name, popularity, release_date, total_tracks, type));
                    }
                    albumRVAdapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get data : " + error, Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                // on below line passing headers.
                HashMap<String, String> headers = new HashMap<>();
                headers.put("Authorization", getToken());
                headers.put("Accept", "application/json");
                headers.put("Content-Type", "application/json");
                return headers;
            }
        };
        // on below line adding request to queue.
        queue.add(albumObjReq);
    }
 
    private void initializeTrendingAlbumsRV() {
        // on below line creating list, initializing adapter
        // and setting it to recycler view.
        RecyclerView albumsRV = findViewById(R.id.idRVTrendingAlbums);
        ArrayList<AlbumRVModal> albumRVModalArrayList = new ArrayList<>();
        AlbumRVAdapter albumRVAdapter = new AlbumRVAdapter(albumRVModalArrayList, this);
        albumsRV.setAdapter(albumRVAdapter);
        // on below line creating a variable for url
        RequestQueue queue = Volley.newRequestQueue(MainActivity.this);
        // on below line making json object request to parse json data.
        JsonObjectRequest albumObjReq = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONArray albumArray = response.getJSONArray("albums");
                    for (int i = 0; i < albumArray.length(); i++) {
                        JSONObject albumObj = albumArray.getJSONObject(i);
                        String album_type = albumObj.getString("album_type");
                        String artistName = albumObj.getJSONArray("artists").getJSONObject(0).getString("name");
                        String external_ids = albumObj.getJSONObject("external_ids").getString("upc");
                        String external_urls = albumObj.getJSONObject("external_urls").getString("spotify");
                        String href = albumObj.getString("href");
                        String id = albumObj.getString("id");
                        String imgUrl = albumObj.getJSONArray("images").getJSONObject(1).getString("url");
                        String label = albumObj.getString("label");
                        String name = albumObj.getString("name");
                        int popularity = albumObj.getInt("popularity");
                        String release_date = albumObj.getString("release_date");
                        int total_tracks = albumObj.getInt("total_tracks");
                        String type = albumObj.getString("type");
                        // on below line adding data to array list.
                        albumRVModalArrayList.add(new AlbumRVModal(album_type, artistName, external_ids, external_urls, href, id, imgUrl, label, name, popularity, release_date, total_tracks, type));
                    }
                    albumRVAdapter.notifyDataSetChanged();
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(MainActivity.this, "Fail to get data : " + error, Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                // on below line passing headers.
                HashMap<String, String> headers = new HashMap<>();
                headers.put("Authorization", getToken());
                headers.put("Accept", "application/json");
                headers.put("Content-Type", "application/json");
                return headers;
            }
        };
        // on below line adding request to queue.
        queue.add(albumObjReq);
    }
 
}


Step 12: Create a new layout file for tracks

Navigate to app>res>layout>Right click on it>New>Layout Resource file and name it as track_rv_item.xml and add the below code to it. Comments are added in the code to get to know it in detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    android:layout_width="match_parent"
    android:layout_height="wrap_content"
    android:layout_margin="4dp"
    android:background="@color/back_color"
    android:padding="4dp">
 
    <!-- text view for displaying track name-->
    <TextView
        android:id="@+id/idTVTrackName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:padding="4dp"
        android:text="Track Name"
        android:textColor="@color/white"
        android:textSize="17sp" />
 
    <!-- text view for displaying artist name-->
    <TextView
        android:id="@+id/idTVTrackArtist"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVTrackName"
        android:padding="4dp"
        android:text="Track Artist"
        android:textColor="@color/white"
        android:textSize="12sp" />
 
    <View
        android:layout_width="match_parent"
        android:layout_height="0.1dp"
        android:layout_marginTop="2dp"
        android:layout_below="@id/idTVTrackArtist"
        android:background="@color/white" />
 
</RelativeLayout>


 Step 13: Working with the activity_album_detail.xml

Navigate to app>res>layout>activity_album_detail.xml and add the below code to it. Comments are added in the code to get to know it in detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/back_color"
    tools:context=".AlbumDetailActivity">
 
    <!-- image view for displaying album image-->
    <ImageView
        android:id="@+id/idIVAlbum"
        android:layout_width="200dp"
        android:layout_height="200dp"
        android:layout_centerHorizontal="true"
        android:layout_marginTop="35dp" />
 
    <!-- text view for displaying album name-->
    <TextView
        android:id="@+id/idTVAlbumName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idIVAlbum"
        android:layout_marginStart="10dp"
        android:layout_marginTop="15dp"
        android:layout_marginEnd="10dp"
        android:layout_toLeftOf="@id/idFABPlay"
        android:paddingStart="10dp"
        android:paddingEnd="10dp"
        android:text="Search"
        android:textColor="@color/white"
        android:textSize="20sp"
        android:textStyle="bold" />
 
    <!-- text view for displaying album artist-->
    <TextView
        android:id="@+id/idTVArtistName"
        android:layout_width="match_parent"
        android:layout_height="wrap_content"
        android:layout_below="@id/idTVAlbumName"
        android:layout_marginStart="10dp"
        android:layout_marginEnd="10dp"
        android:layout_toLeftOf="@id/idFABPlay"
        android:paddingStart="10dp"
        android:paddingTop="4dp"
        android:paddingEnd="10dp"
        android:paddingBottom="4dp"
        android:text="Artist Name"
        android:textColor="@color/white"
        android:textSize="12sp"
        android:textStyle="bold" />
 
    <!-- fab to play album-->
    <com.google.android.material.floatingactionbutton.FloatingActionButton
        android:id="@+id/idFABPlay"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_below="@id/idIVAlbum"
        android:layout_alignParentEnd="true"
        android:layout_alignParentRight="true"
        android:layout_marginStart="15dp"
        android:layout_marginTop="15dp"
        android:layout_marginEnd="15dp"
        android:padding="10dp"
        android:src="@drawable/ic_play"
        android:tint="@color/fab_color"
        app:backgroundTint="@color/fab_color"
        app:fabSize="mini"
        app:tint="@color/black" />
 
    <!-- recycler view to display tracks-->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/idRVTracks"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/idTVArtistName"
        android:layout_margin="4dp"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/track_rv_item" />
 
</RelativeLayout>


Step 14: Create a modal class for track details

Navigate to app>java>your app’s package name>Right click on it>New>Java class and name it as TrackRVModal and add the below code to it. Comments are added in the code to get to know it in detail.

Java




package com.gtappdevelopers.spotify_java;
 
public class TrackRVModal {
    // on below line creating variables
    // for track name, track artist and id.
    private String trackName;
    private String trackArtist;
    private String id;
 
    // on below line creating
    // constructors, getters and setters.
    public String getTrackName() {
        return trackName;
    }
 
    public void setTrackName(String trackName) {
        this.trackName = trackName;
    }
 
    public String getTrackArtist() {
        return trackArtist;
    }
 
    public void setTrackArtist(String trackArtist) {
        this.trackArtist = trackArtist;
    }
 
    public String getId() {
        return id;
    }
 
    public void setId(String id) {
        this.id = id;
    }
 
    public TrackRVModal(String trackName, String trackArtist, String id) {
        this.trackName = trackName;
        this.trackArtist = trackArtist;
        this.id = id;
    }
}


Step 15: Creating an adapter class for Tracks RecyclerView

Navigate to app>java>your app’s package name>Right click on it>New>Java class and name it as TrackRVAdapter and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
import android.content.Context;
import android.content.Intent;
import android.net.Uri;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
 
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
 
import java.util.ArrayList;
 
public class TrackRVAdapter extends RecyclerView.Adapter<TrackRVAdapter.ViewHolder> {
    // creating variables for list and context
    private ArrayList<TrackRVModal> trackRVModals;
    private Context context;
 
    // creating constructor on below line.
    public TrackRVAdapter(ArrayList<TrackRVModal> trackRVModals, Context context) {
        this.trackRVModals = trackRVModals;
        this.context = context;
    }
 
    @NonNull
    @Override
    public TrackRVAdapter.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
        // inflating layout on below line.
        View view = LayoutInflater.from(parent.getContext()).inflate(R.layout.track_rv_item, parent, false);
        return new ViewHolder(view);
    }
 
    @Override
    public void onBindViewHolder(@NonNull TrackRVAdapter.ViewHolder holder, int position) {
        // setting data to text views.
        TrackRVModal trackRVModal = trackRVModals.get(position);
        holder.trackNameTV.setText(trackRVModal.getTrackName());
        holder.trackArtistTV.setText(trackRVModal.getTrackArtist());
        // adding click listener for track item view
        holder.itemView.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String trackUrl = "https://open.spotify.com/track/" + trackRVModal.getId();
                Uri uri = Uri.parse(trackUrl); // missing 'http://' will cause crashed
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                context.startActivity(intent);
            }
        });
    }
 
    @Override
    public int getItemCount() {
        return trackRVModals.size();
    }
 
    public class ViewHolder extends RecyclerView.ViewHolder {
        // creating and initializing variables for text views.
        private TextView trackNameTV, trackArtistTV;
 
        public ViewHolder(@NonNull View itemView) {
            super(itemView);
            trackNameTV = itemView.findViewById(R.id.idTVTrackName);
            trackArtistTV = itemView.findViewById(R.id.idTVTrackArtist);
        }
    }
}


Step 16: Working with the AlbumDetailActivity.java file

Navigate to app>java>your app’s package name>AlbumDetailActivity.java and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
 
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
 
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
import com.google.android.material.floatingactionbutton.FloatingActionButton;
import com.squareup.picasso.Picasso;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
public class AlbumDetailActivity extends AppCompatActivity {
 
    // creating variables on below line.
    String albumID = "";
    String albumImgUrl, albumName, artist, albumUrl;
 
    private TextView albumNameTV, artistTV;
    private ImageView albumIV;
    private FloatingActionButton playFAB;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
         
        // initializing variables on below line.
        setContentView(R.layout.activity_album_detail);
        albumID = getIntent().getStringExtra("id");
        albumIV = findViewById(R.id.idIVAlbum);
        albumImgUrl = getIntent().getStringExtra("img");
        albumName = getIntent().getStringExtra("name");
        artist = getIntent().getStringExtra("artist");
        albumUrl = getIntent().getStringExtra("albumUrl");
        Log.e("TAG", "album id is : " + albumID);
        albumNameTV = findViewById(R.id.idTVAlbumName);
        playFAB = findViewById(R.id.idFABPlay);
        artistTV = findViewById(R.id.idTVArtistName);
        // setting data on below line.
        albumNameTV.setText(albumName);
        artistTV.setText(artist);
        // adding click listener for fab on below line.
        playFAB.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // opening album from url on below line.
                Uri uri = Uri.parse(albumUrl); // missing 'http://' will cause crashed
                Intent intent = new Intent(Intent.ACTION_VIEW, uri);
                startActivity(intent);
            }
        });
        // loading image on below line.
        Picasso.get().load(albumImgUrl).into(albumIV);
        // getting album tracks on below line.
        getAlbumTracks(albumID);
    }
 
    // method to get access token.
    private String getToken() {
        SharedPreferences sh = getSharedPreferences("MySharedPref", MODE_PRIVATE);
        return sh.getString("token", "Not Found");
    }
 
    // method to get tracks from albums.
    private void getAlbumTracks(String albumID) {
        // on below line creating variable for url
        String url = "https://api.spotify.com/v1/albums/" + albumID + "/tracks";
        // on below line creating list, initializing adapter and setting it to recycler view.
        ArrayList<TrackRVModal> trackRVModals = new ArrayList<>();
        TrackRVAdapter trackRVAdapter = new TrackRVAdapter(trackRVModals, this);
        RecyclerView trackRV = findViewById(R.id.idRVTracks);
        trackRV.setAdapter(trackRVAdapter);
        RequestQueue queue = Volley.newRequestQueue(AlbumDetailActivity.this);
        // on below line making json object request to parse json data.
        JsonObjectRequest trackObj = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONArray itemsArray = response.getJSONArray("items");
                    for (int i = 0; i < itemsArray.length(); i++) {
                        JSONObject itemObj = itemsArray.getJSONObject(i);
                        String trackName = itemObj.getString("name");
                        String id = itemObj.getString("id");
                        String trackArtist = itemObj.getJSONArray("artists").getJSONObject(0).getString("name");
                        // on below line adding data to array list.
                        trackRVModals.add(new TrackRVModal(trackName, trackArtist, id));
                    }
                    trackRVAdapter.notifyDataSetChanged();
 
                } catch (JSONException e) {
                    e.printStackTrace();
                }
 
 
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(AlbumDetailActivity.this, "Fail to get Tracks" + error, Toast.LENGTH_SHORT).show();
            }
        }) {
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                // on below line passing headers.
                HashMap<String, String> headers = new HashMap<>();
                headers.put("Authorization", getToken());
                headers.put("Accept", "application/json");
                headers.put("Content-Type", "application/json");
                return headers;
            }
        };
        // on below line adding
        // request to queue.
        queue.add(trackObj);
    }
}


Step 17: Working with the activity_search.xml file

Navigate to app>res>layout>activity_search.xml file and add the below code to it. Comments are added in the code to get to know it in detail. 

XML




<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout
    xmlns:tools="http://schemas.android.com/tools"
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:background="@color/back_color"
    tools:context=".SearchActivity">
 
    <!-- edit text to search songs-->
    <EditText
        android:id="@+id/idEdtSearch"
        android:layout_width="match_parent"
        android:layout_height="50dp"
        android:layout_marginBottom="5dp"
        android:background="#545454"
        android:hint="Search song"
        android:imeOptions="actionDone"
        android:lines="1"
        android:padding="10dp"
        android:singleLine="true"
        android:textColor="@color/white"
        android:textColorHint="@android:color/white"
        android:textStyle="bold" />
 
    <!-- recycler view t display search results-->
    <androidx.recyclerview.widget.RecyclerView
        android:id="@+id/idRVSongs"
        android:layout_width="match_parent"
        android:layout_height="match_parent"
        android:layout_below="@id/idEdtSearch"
        android:orientation="vertical"
        app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
        tools:listitem="@layout/track_rv_item" />
 
</RelativeLayout>


Step 18: Working with the SearchActivity.java file

Navigate to app>java>your app’s package name>SearchActivity.java file and add the below code to it. Comments are added in the code to get to know it in detail. 

Java




package com.gtappdevelopers.spotify_java;
 
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
 
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
 
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
import com.android.volley.RequestQueue;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.android.volley.toolbox.Volley;
 
import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
 
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Map;
 
public class SearchActivity extends AppCompatActivity {
 
    // on below line creating variables
    String searchQuery = "";
    private EditText searchEdt;
 
    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_search);
        // on below line initializing variables.
        searchEdt = findViewById(R.id.idEdtSearch);
        searchQuery = getIntent().getStringExtra("searchQuery");
        searchEdt.setText(searchQuery);
        // on below line adding action listener
        // for search edit text
        searchEdt.setOnEditorActionListener(new EditText.OnEditorActionListener() {
            @Override
            public boolean onEditorAction(TextView v, int actionId, KeyEvent event) {
                if (actionId == EditorInfo.IME_ACTION_DONE) {
                    // on below line calling method to get tracks.
                    getTracks(searchEdt.getText().toString());
                    return true;
                }
                return false;
            }
        });
        // on below line getting tracks.
        getTracks(searchQuery);
 
    }
 
    // method to get token.
    private String getToken() {
        SharedPreferences sh = getSharedPreferences("MySharedPref", MODE_PRIVATE);
        return sh.getString("token", "Not Found");
    }
 
 
    private void getTracks(String searchQuery) {
        // on below line creating and initializing variables
        // for recycler view,list and adapter.
        RecyclerView songsRV = findViewById(R.id.idRVSongs);
        ArrayList<TrackRVModal> trackRVModals = new ArrayList<>();
        TrackRVAdapter trackRVAdapter = new TrackRVAdapter(trackRVModals, this);
        songsRV.setAdapter(trackRVAdapter);
        // on below line creating variable for url.
        String url = "https://api.spotify.com/v1/search?q=" + searchQuery + "&type=track";
        RequestQueue queue = Volley.newRequestQueue(SearchActivity.this);
        // on below line making json object request
        JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(Request.Method.GET, url, null, new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                try {
                    JSONObject trackObj = response.getJSONObject("tracks");
                    JSONArray itemsArray = trackObj.getJSONArray("items");
                    for (int i = 0; i < itemsArray.length(); i++) {
                        JSONObject itemObj = itemsArray.getJSONObject(i);
                        String trackName = itemObj.getString("name");
                        String trackArtist = itemObj.getJSONArray("artists").getJSONObject(0).getString("name");
                        String trackID = itemObj.getString("id");
                        // on below line adding data to array list
                        trackRVModals.add(new TrackRVModal(trackName, trackArtist, trackID));
                    }
                    // on below line notifying adapter
                    trackRVAdapter.notifyDataSetChanged();
 
                } catch (JSONException e) {
                    e.printStackTrace();
                }
 
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Toast.makeText(SearchActivity.this, "Fail to get data : " + error, Toast.LENGTH_SHORT).show();
            }
        }) {
 
            @Override
            public Map<String, String> getHeaders() throws AuthFailureError {
                // on below line adding headers.
                HashMap<String, String> headers = new HashMap<>();
                headers.put("Authorization", getToken());
                headers.put("Accept", "application/json");
                headers.put("Content-Type", "application/json");
                return headers;
            }
        };
        // adding json object request to queue.
        queue.add(jsonObjectRequest);
 
    }
}


Step 19: Adding internet permissions

Navigate to app > AndroidManifest.xml file and add below internet permissions to it. 

XML




<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />


Now run your project to see the output of your project. 

Output:

Dominic Rubhabha-Wardslaus
Dominic Rubhabha-Wardslaushttp://wardslaus.com
infosec,malicious & dos attacks generator, boot rom exploit philanthropist , wild hacker , game developer,
RELATED ARTICLES

Most Popular

Recent Comments