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.

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.
Step 2: Adding dependency
Navigate to Gradle Scripts > build.gradle.kts (Module :app) file and add the below dependency to it in the dependencies section.
dependencies {
...
implementation ("com.android.volley:volley:1.2.1")
implementation ("com.github.bumptech.glide:glide:4.16.0")
}
After adding the above dependency simply sync your project to install it.
Step 3: Adding internet permissions
Navigate to app > AndroidManifest.xml file and add below internet permissions to it.
<uses-permission android:name="android.permission.INTERNET" />Step 4: Get Spotify client id and client secret key
- Navigate to Spotify Developer Console.
- Login with your existing spotify account or create a new account in Spotify - Signup.
- Click on your profile picture in the top right corner of the screen and select Dashboard or just go to Spotify for Developers - Dashboard.
- Now you will get to see an option Create 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 client id and client secret key. We have to simply copy the client id and client secret key.
We will be using these 2 parameters in our MainActivity file in our project in Step 10.
Step 5: Updating colors.xml
Navigate to app > res > values > colors.xml file and add the below colors to it. Below is the code for the colors.xml file.
colors.xml:
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="black">#FF000000</color>
<color name="white">#FFFFFFFF</color>
<color name="grey">#BDBDBD</color>
<color name="darker_grey">#808080</color>
<color name="fab_color">#20D761</color>
</resources>
Step 6: Adding drawables
Navigate to app > res > drawables, right click on the folder and choose New > Drawable Resource File and create two such files as play.xml and search.xml. Then, add the following code to both of the files respectively.
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="32dp"
android:height="32dp"
android:viewportWidth="283"
android:viewportHeight="283">
<path
android:pathData="M211.99,211.9C250.99,172.9 250.99,109.67 211.99,70.67C172.99,31.68 109.76,31.68 70.77,70.67C31.77,109.67 31.77,172.9 70.77,211.9C109.76,250.89 172.99,250.89 211.99,211.9Z"
android:fillColor="@color/fab_color"/>
<path
android:pathData="M197.5,141.29L113.32,192.72L113.32,89.85L197.5,141.29Z"
android:fillColor="#ffffff"/>
</vector>
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:pathData="M784,840 L532,588q-30,24 -69,38t-83,14q-109,0 -184.5,-75.5T120,380q0,-109 75.5,-184.5T380,120q109,0 184.5,75.5T640,380q0,44 -14,83t-38,69l252,252 -56,56ZM380,560q75,0 127.5,-52.5T560,380q0,-75 -52.5,-127.5T380,200q-75,0 -127.5,52.5T200,380q0,75 52.5,127.5T380,560Z"
android:fillColor="#e8eaed"/>
</vector>
Step 7: Working with activity_main.xml
Navigate to app > res > layout > activity_main.xml and add the following lines of code. Then, create a layout, album_rv_item.xml and add the below code.
<?xml version="1.0" encoding="utf-8"?>
<ScrollView
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="match_parent"
android:background="@color/white"
tools:context=".MainActivity">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/white"
android:orientation="vertical">
<!-- text view to display greeting-->
<TextView
android:id="@+id/idTVGreetHeading"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="Good morning"
android:layout_marginStart="24dp"
android:layout_marginTop="32dp"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold" />
<!-- edit text to search songs-->
<EditText
android:id="@+id/idEdtSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginEnd="24dp"
android:layout_marginTop="16dp"
android:background="@color/grey"
android:drawableStart="@drawable/search"
android:drawablePadding="8dp"
android:drawableTint="@color/white"
android:textColor="@color/white"
android:hint="What do you want to listen to?"
android:imeOptions="actionDone"
android:lines="1"
android:padding="10dp"
android:singleLine="true"
android:textColorHint="@color/white"
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_marginStart="24dp"
android:paddingTop="16dp"
android:text="Recommended for Today"
android:textColor="@color/black"
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:orientation="horizontal"
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_marginStart="24dp"
android:layout_marginTop="16dp"
android:paddingTop="16dp"
android:text="Popular Albums"
android:textColor="@color/black"
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:orientation="horizontal"
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_marginStart="24dp"
android:layout_marginTop="16dp"
android:paddingTop="16dp"
android:text="Trending now"
android:textColor="@color/black"
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:orientation="horizontal"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/album_rv_item" />
</LinearLayout>
</ScrollView>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_centerInParent="true"
android:orientation="vertical"
android:background="@color/white"
android:paddingTop="8dp"
android:layout_marginStart="24dp">
<!--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_marginTop="3dp"
android:lines="1"
android:maxLines="1"
android:text="Album Name"
android:textColor="@color/black"
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_marginTop="3dp"
android:maxLines="1"
android:text="Album Details"
android:textColor="@color/black"
android:textSize="12sp" />
</LinearLayout>
Design UI:

Step 8: Create an album model class
Create a Java/Kotlin data class file AlbumModel and add the following code
package org.geeksforgeeks.demo;
public class AlbumModel {
private String album_type;
private String artistName;
private String external_ids;
private String external_urls;
private String href;
private String id;
private String imageUrl;
private String label;
private String name;
private int popularity;
private String release_date;
private int total_tracks;
private String type;
public AlbumModel(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;
}
// Add getters (and setters if needed)
public String getAlbum_type() { return album_type; }
public String getArtistName() { return artistName; }
public String getExternal_ids() { return external_ids; }
public String getExternal_urls() { return external_urls; }
public String getHref() { return href; }
public String getId() { return id; }
public String getImageUrl() { return imageUrl; }
public String getLabel() { return label; }
public String getName() { return name; }
public int getPopularity() { return popularity; }
public String getRelease_date() { return release_date; }
public int getTotal_tracks() { return total_tracks; }
public String getType() { return type; }
package org.geeksforgeeks.demo
data class AlbumModel(
val album_type: String,
val artistName: String,
val external_ids: String,
val external_urls: String,
val href: String,
val id: String,
val imageUrl: String,
val label: String,
val name: String,
val popularity: Int,
val release_date: String,
val total_tracks: Int,
val type: String
)
Step 9: Creating an Adapter for each album
Create a new Java/Kotlin file AlbumAdapter and add the following code
package org.geeksforgeeks.demo;
import android.content.Context;
import android.content.Intent;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView;
import com.bumptech.glide.Glide;
import org.geeksforgeeks.demo.databinding.AlbumRvItemBinding;
import java.util.List;
public class AlbumAdapter extends RecyclerView.Adapter<AlbumAdapter.AlbumViewHolder> {
private final List<AlbumModel> albums;
private final Context context;
public AlbumAdapter(List<AlbumModel> albums, Context context) {
this.albums = albums;
this.context = context;
}
@NonNull
@Override
public AlbumViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
LayoutInflater inflater = LayoutInflater.from(parent.getContext());
AlbumRvItemBinding binding = AlbumRvItemBinding.inflate(inflater, parent, false);
return new AlbumViewHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull AlbumViewHolder holder, int position) {
AlbumModel album = albums.get(position);
holder.bind(album);
holder.itemView.setOnClickListener(v -> {
Intent intent = new Intent(context, AlbumDetailActivity.class);
intent.putExtra("id", album.getId());
intent.putExtra("name", album.getName());
intent.putExtra("img", album.getImageUrl());
intent.putExtra("artist", album.getArtistName());
intent.putExtra("albumUrl", album.getExternal_urls());
context.startActivity(intent);
});
}
@Override
public int getItemCount() {
return albums.size();
}
public class AlbumViewHolder extends RecyclerView.ViewHolder {
private final AlbumRvItemBinding binding;
public AlbumViewHolder(AlbumRvItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
public void bind(AlbumModel album) {
Glide.with(context)
.load(album.getImageUrl())
.into(binding.idIVAlbum);
binding.idTVAlbumName.setText(album.getName());
binding.idTVALbumDetails.setText(album.getArtistName());
}
}
}
package org.geeksforgeeks.demo
import android.content.Context
import android.content.Intent
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.bumptech.glide.Glide
import org.geeksforgeeks.demo.databinding.AlbumRvItemBinding
class AlbumAdapter(
private val albums: List<AlbumModel>,
private val context: Context
) : RecyclerView.Adapter<AlbumAdapter.AlbumViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AlbumViewHolder {
val binding = AlbumRvItemBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return AlbumViewHolder(binding)
}
override fun onBindViewHolder(holder: AlbumViewHolder, position: Int) {
val album = albums[position]
holder.bind(album)
holder.itemView.setOnClickListener {
context.startActivity(
Intent(context, AlbumDetailActivity::class.java).apply {
putExtra("id", album.id)
putExtra("name", album.name)
putExtra("img", album.imageUrl)
putExtra("artist", album.artistName)
putExtra("albumUrl", album.external_urls)
}
)
}
}
override fun getItemCount() = albums.size
inner class AlbumViewHolder(private val binding: AlbumRvItemBinding) :
RecyclerView.ViewHolder(binding.root) {
fun bind(album: AlbumModel) {
Glide.with(context)
.load(album.imageUrl)
.into(binding.idIVAlbum)
binding.idTVAlbumName.text = album.name
binding.idTVALbumDetails.text = album.artistName
}
}
}
Step 10: Working with MainActivity
Navigate to app > java > {package-name} > MainActivity.kt/.java and add the following code. Here we will be writing the code for the spotify home page.
package org.geeksforgeeks.demo;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.util.Base64;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.LinearLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.android.volley.AuthFailureError;
import com.android.volley.Request;
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.List;
import java.util.Map;
public class MainActivity extends AppCompatActivity {
private RecyclerView albumsRV, popularAlbumsRV, trendingAlbumsRV;
private EditText searchEdt;
private boolean isTokenGenerated = false;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initializeViews(); // Link UI elements with code
setupSearchView(); // Set listener for the search action
generateToken(); // Get Spotify API token
}
private void initializeViews() {
albumsRV = findViewById(R.id.idRVAlbums);
popularAlbumsRV = findViewById(R.id.idRVPopularAlbums);
trendingAlbumsRV = findViewById(R.id.idRVTrendingAlbums);
searchEdt = findViewById(R.id.idEdtSearch);
// Set horizontal layout for each RecyclerView
RecyclerView[] recyclerViews = {albumsRV, popularAlbumsRV, trendingAlbumsRV};
for (RecyclerView rv : recyclerViews) {
rv.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
rv.setHasFixedSize(true);
}
}
private void setupSearchView() {
searchEdt.setOnEditorActionListener((v, actionId, event) -> {
if (actionId == EditorInfo.IME_ACTION_DONE) {
searchTracks(searchEdt.getText().toString().trim());
return true;
}
return false;
});
}
private void searchTracks(String searchQuery) {
if (!searchQuery.isEmpty()) {
// Navigate to SearchActivity with query
Intent intent = new Intent(this, SearchActivity.class);
intent.putExtra("searchQuery", searchQuery);
startActivity(intent);
} else {
Toast.makeText(this, "Please enter a search term", Toast.LENGTH_SHORT).show();
}
}
private void generateToken() {
String url = "https://accounts.spotify.com/api/token";
StringRequest request = new StringRequest(Request.Method.POST, url,
response -> {
try {
JSONObject jsonObject = new JSONObject(response);
String token = jsonObject.getString("access_token");
// Save token in SharedPreferences
SharedPreferences sharedPreferences = getSharedPreferences("MySharedPref", MODE_PRIVATE);
sharedPreferences.edit().putString("token", "Bearer " + token).apply();
isTokenGenerated = true;
loadAllAlbumData(); // Load albums after token is ready
} catch (JSONException e) {
showError("Failed to parse token response: " + e.getMessage());
}
},
error -> showError("Failed to get token: " + error.getMessage())
) {
@Override
public Map<String, String> getHeaders() {
String clientId = "Enter your own client id";
String clientSecret = "Enter your own client secret";
String credentials = clientId + ":" + clientSecret;
String auth = Base64.encodeToString(credentials.getBytes(), Base64.NO_WRAP);
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", "Basic " + auth);
headers.put("Content-Type", "application/x-www-form-urlencoded");
return headers;
}
@Override
protected Map<String, String> getParams() {
Map<String, String> params = new HashMap<>();
params.put("grant_type", "client_credentials");
return params;
}
};
Volley.newRequestQueue(this).add(request);
}
private void loadAllAlbumData() {
if (!isTokenGenerated) {
showError("Token not generated yet");
return;
}
// Load Recommended Albums
loadAlbums(albumsRV, List.of(
"5Nwsra93UQYJ6xxcjcE10x", "0z7bJ6UpjUw8U4TATtc5Ku", "36UJ90D0e295TvlU109Xvy",
"3uuu6u13U0KeVQsZ3CZKK4", "45ZIondgVoMB84MQQaUo9T", "15CyNDuGY5fsG0Hn9rjnpG",
"1HeX4SmCFW4EPHQDvHgrVS", "6mCDTT1XGTf48p6FkK9qFL"
));
// Load Popular Albums
loadAlbums(popularAlbumsRV, List.of(
"0sjyZypccO1vyihqaAkdt3", "17vZRWjKOX7TmMktjQL2Qx", "5Nwsra93UQYJ6xxcjcE10x",
"2zXKlf81VmDHIMtQe3oD0r", "7Gws1vUsWltRs58x8QuYVQ", "7uftfPn8f7lwtRLUrEVRYM",
"7kSY0fqrPep5vcwOb1juye"
));
// Load Trending Albums
loadAlbums(trendingAlbumsRV, List.of(
"1P4eCx5b11Tfmi4s1GmWmQ", "2SsEtiB6yJYn8hRRAmtVda", "7hhxms8KCwlQCWffIJpN9b",
"3umvKIjsD484pa9pCyPK2x", "3OHC6XD29wXWADtAOP2geV", "3RZxrS2dDZlbsYtMRM89v8",
"24C47633GRlozws7WBth7t"
));
}
private void loadAlbums(RecyclerView recyclerView, List<String> albumIds) {
SharedPreferences sharedPreferences = getSharedPreferences("MySharedPref", MODE_PRIVATE);
String token = sharedPreferences.getString("token", "");
if (token.isEmpty()) {
showError("Authentication token is missing");
return;
}
String url = "https://api.spotify.com/v1/albums?ids=" + String.join(",", albumIds);
JsonObjectRequest request = new JsonObjectRequest(Request.Method.GET, url, null,
response -> {
try {
parseAlbumResponse(response, recyclerView);
} catch (Exception e) {
showError("Failed to parse album data: " + e.getMessage());
}
},
error -> showError("Failed to load albums: " + error.getMessage())
) {
@Override
public Map<String, String> getHeaders() {
Map<String, String> headers = new HashMap<>();
headers.put("Authorization", token);
headers.put("Accept", "application/json");
headers.put("Content-Type", "application/json");
return headers;
}
};
Volley.newRequestQueue(this).add(request);
}
private void parseAlbumResponse(JSONObject response, RecyclerView recyclerView) {
try {
JSONArray albumArray = response.getJSONArray("albums");
List<AlbumModel> albumList = new ArrayList<>();
for (int i = 0; i < albumArray.length(); i++) {
JSONObject albumObj = albumArray.getJSONObject(i);
JSONArray artists = albumObj.getJSONArray("artists");
JSONArray images = albumObj.getJSONArray("images");
String artistName = (artists.length() > 0)
? artists.getJSONObject(0).optString("name", "Unknown Artist")
: "Unknown Artist";
String imageUrl = (images.length() > 1)
? images.getJSONObject(1).optString("url", "")
: "";
albumList.add(new AlbumModel(
albumObj.optString("album_type", "album"),
artistName,
albumObj.getJSONObject("external_ids").optString("upc", ""),
albumObj.getJSONObject("external_urls").optString("spotify", ""),
albumObj.optString("href", ""),
albumObj.optString("id", ""),
imageUrl,
albumObj.optString("label", ""),
albumObj.optString("name", "Unknown Album"),
albumObj.optInt("popularity", 0),
albumObj.optString("release_date", ""),
albumObj.optInt("total_tracks", 0),
albumObj.optString("type", "album")
));
}
recyclerView.setAdapter(new AlbumAdapter(albumList, this));
} catch (JSONException e) {
showError("JSON parsing error: " + e.getMessage());
} catch (Exception e) {
showError("Error parsing album data: " + e.getMessage());
}
}
private void showError(String message) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show();
}
}
package org.geeksforgeeks.demo
import android.content.Intent
import android.os.Bundle
import android.util.Base64
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.Request
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.StringRequest
import com.android.volley.toolbox.Volley
import org.json.JSONException
import org.json.JSONObject
class MainActivity : AppCompatActivity() {
private lateinit var albumsRV: RecyclerView
private lateinit var popularAlbumsRV: RecyclerView
private lateinit var trendingAlbumsRV: RecyclerView
private lateinit var searchEdt: EditText
private var isTokenGenerated = false
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_main)
initializeViews()
setupSearchView()
generateToken()
}
private fun initializeViews() {
albumsRV = findViewById(R.id.idRVAlbums)
popularAlbumsRV = findViewById(R.id.idRVPopularAlbums)
trendingAlbumsRV = findViewById(R.id.idRVTrendingAlbums)
searchEdt = findViewById(R.id.idEdtSearch)
// Setup RecyclerViews with horizontal layout
listOf(albumsRV, popularAlbumsRV, trendingAlbumsRV).forEach { recyclerView ->
recyclerView.layoutManager = LinearLayoutManager(
this,
LinearLayoutManager.HORIZONTAL,
false
)
recyclerView.setHasFixedSize(true)
}
}
private fun setupSearchView() {
searchEdt.setOnEditorActionListener { _, actionId, _ ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
searchTracks(searchEdt.text.toString().trim())
true
} else {
false
}
}
}
private fun searchTracks(searchQuery: String) {
if (searchQuery.isNotEmpty()) {
startActivity(
Intent(this, SearchActivity::class.java).apply {
putExtra("searchQuery", searchQuery)
}
)
} else {
Toast.makeText(this, "Please enter a search term", Toast.LENGTH_SHORT).show()
}
}
private fun generateToken() {
val url = "https://accounts.spotify.com/api/token"
val queue = Volley.newRequestQueue(this)
val request = object : StringRequest(
Request.Method.POST, url,
{ response ->
try {
val token = JSONObject(response).getString("access_token")
getSharedPreferences("MySharedPref", MODE_PRIVATE).edit {
putString("token", "Bearer $token")
apply()
}
isTokenGenerated = true
// Load data after token is generated
loadAllAlbumData()
} catch (e: JSONException) {
showError("Failed to parse token response: ${e.message}")
}
},
{ error -> showError("Failed to get token: ${error.message}") }
) {
override fun getHeaders(): Map<String, String> {
val clientId = "Enter your own client id"
val clientSecret = "Enter your own client secret"
val credentials = "$clientId:$clientSecret"
val auth = Base64.encodeToString(credentials.toByteArray(), Base64.NO_WRAP)
return mapOf(
"Authorization" to "Basic $auth",
"Content-Type" to "application/x-www-form-urlencoded"
)
}
override fun getParams(): MutableMap<String, String> {
return mutableMapOf("grant_type" to "client_credentials")
}
}
queue.add(request)
}
private fun loadAllAlbumData() {
if (!isTokenGenerated) {
showError("Token not generated yet")
return
}
// Recommended albums
loadAlbums(
recyclerView = albumsRV,
albumIds = listOf(
"5Nwsra93UQYJ6xxcjcE10x",
"0z7bJ6UpjUw8U4TATtc5Ku",
"36UJ90D0e295TvlU109Xvy",
"3uuu6u13U0KeVQsZ3CZKK4",
"45ZIondgVoMB84MQQaUo9T",
"15CyNDuGY5fsG0Hn9rjnpG",
"1HeX4SmCFW4EPHQDvHgrVS",
"6mCDTT1XGTf48p6FkK9qFL"
)
)
// Popular albums
loadAlbums(
recyclerView = popularAlbumsRV,
albumIds = listOf(
"0sjyZypccO1vyihqaAkdt3",
"17vZRWjKOX7TmMktjQL2Qx",
"5Nwsra93UQYJ6xxcjcE10x",
"2zXKlf81VmDHIMtQe3oD0r",
"7Gws1vUsWltRs58x8QuYVQ",
"7uftfPn8f7lwtRLUrEVRYM",
"7kSY0fqrPep5vcwOb1juye"
)
)
// Trending albums
loadAlbums(
recyclerView = trendingAlbumsRV,
albumIds = listOf(
"1P4eCx5b11Tfmi4s1GmWmQ",
"2SsEtiB6yJYn8hRRAmtVda",
"7hhxms8KCwlQCWffIJpN9b",
"3umvKIjsD484pa9pCyPK2x",
"3OHC6XD29wXWADtAOP2geV",
"3RZxrS2dDZlbsYtMRM89v8",
"24C47633GRlozws7WBth7t"
)
)
}
private fun loadAlbums(recyclerView: RecyclerView, albumIds: List<String>) {
val token = getSharedPreferences("MySharedPref", MODE_PRIVATE)
.getString("token", "") ?: ""
if (token.isEmpty()) {
showError("Authentication token is missing")
return
}
val url = "https://api.spotify.com/v1/albums?ids=$%7BalbumIds.joinToString(",")}"
val queue = Volley.newRequestQueue(this)
val request = object : JsonObjectRequest(
Request.Method.GET, url, null,
{ response ->
try {
parseAlbumResponse(response, recyclerView)
} catch (e: Exception) {
showError("Failed to parse album data: ${e.message}")
}
},
{ error ->
showError("Failed to load albums: ${error.message}")
}
) {
override fun getHeaders(): Map<String, String> {
return mapOf(
"Authorization" to token,
"Accept" to "application/json",
"Content-Type" to "application/json"
)
}
}
queue.add(request)
}
private fun parseAlbumResponse(response: JSONObject, recyclerView: RecyclerView) {
try {
val albumArray = response.getJSONArray("albums")
val albumList = mutableListOf<AlbumModel>()
for (i in 0 until albumArray.length()) {
val albumObj = albumArray.getJSONObject(i)
val artists = albumObj.getJSONArray("artists")
val images = albumObj.getJSONArray("images")
albumList.add(AlbumModel(
album_type = albumObj.optString("album_type", "album"),
artistName = if (artists.length() > 0)
artists.getJSONObject(0).optString("name", "Unknown Artist")
else "Unknown Artist",
external_ids = albumObj.getJSONObject("external_ids").optString("upc", ""),
external_urls = albumObj.getJSONObject("external_urls").optString("spotify", ""),
href = albumObj.optString("href", ""),
id = albumObj.optString("id", ""),
imageUrl = if (images.length() > 1)
images.getJSONObject(1).optString("url", "")
else "",
label = albumObj.optString("label", ""),
name = albumObj.optString("name", "Unknown Album"),
popularity = albumObj.optInt("popularity", 0),
release_date = albumObj.optString("release_date", ""),
total_tracks = albumObj.optInt("total_tracks", 0),
type = albumObj.optString("type", "album")
))
}
recyclerView.adapter = AlbumAdapter(albumList, this)
} catch (e: JSONException) {
showError("JSON parsing error: ${e.message}")
} catch (e: Exception) {
showError("Error parsing album data: ${e.message}")
}
}
private fun showError(message: String) {
Toast.makeText(this, message, Toast.LENGTH_LONG).show()
}
}
Step 11: Create a new Activity (Search Activity)
Create a new activity with the name SearchActivity. Now, navigate to app > res > layout > activity_search.xml and add the following code. Then, create a layout track_rv_item.xml and add the following code respectively.
<?xml version="1.0" encoding="utf-8"?>
<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="match_parent"
android:background="@color/white"
android:orientation="vertical"
tools:context=".SearchActivity">
<!-- edit text to search songs-->
<EditText
android:id="@+id/idEdtSearch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="@color/darker_grey"
android:hint="What do you want to listen to?"
android:imeOptions="actionDone"
android:lines="1"
android:drawableStart="@drawable/search"
android:drawablePadding="8dp"
android:drawableTint="@color/grey"
android:padding="16dp"
android:singleLine="true"
android:textColor="@color/white"
android:textColorHint="@color/grey"
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:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
tools:listitem="@layout/track_rv_item" />
</LinearLayout>
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp"
android:layout_marginHorizontal="8dp"
android:background="@color/white"
android:orientation="vertical"
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:text="Track Name"
android:textColor="@color/black"
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:text="Track Artist"
android:textColor="@color/darker_grey"
android:textSize="12sp" />
</LinearLayout>
Design UI:

Step 12: Create a track model class
Create a Java/Kotlin data class file TrackModel and add the following code
package org.geeksforgeeks.demo;
public class TrackModel {
private String trackName;
private String trackArtist;
private String id;
// Constructor
public TrackModel(String trackName, String trackArtist, String id) {
this.trackName = trackName;
this.trackArtist = trackArtist;
this.id = id;
}
// Getters
public String getTrackName() {
return trackName;
}
public String getTrackArtist() {
return trackArtist;
}
public String getId() {
return id;
}
// Setters
public void setTrackName(String trackName) {
this.trackName = trackName;
}
public void setTrackArtist(String trackArtist) {
this.trackArtist = trackArtist;
}
public void setId(String id) {
this.id = id;
}
}
package org.geeksforgeeks.demo
class TrackModel(
var trackName: String,
var trackArtist: String,
var id: String
)
Step 13: Create an Adapter for each music
Create a Java/Kotlin file TrackAdapter and add the following code.
package org.geeksforgeeks.demo;
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 TrackAdapter extends RecyclerView.Adapter<TrackAdapter.ViewHolder> {
private final ArrayList<TrackModel> trackModels;
private final Context context;
public TrackAdapter(ArrayList<TrackModel> trackModels, Context context) {
this.trackModels = trackModels;
this.context = context;
}
@NonNull
@Override
public 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 ViewHolder holder, int position) {
// setting data to text views.
TrackModel trackRVModal = trackModels.get(position);
holder.trackNameTV.setText(trackRVModal.getTrackName());
holder.trackArtistTV.setText(trackRVModal.getTrackArtist());
// adding click listener for track item view
holder.itemView.setOnClickListener(v -> {
String trackUrl = "https://open.spotify.com/track/" + trackRVModal.getId();
Uri uri = Uri.parse(trackUrl); // missing 'http://' will cause crash
Intent intent = new Intent(Intent.ACTION_VIEW, uri);
context.startActivity(intent);
});
}
@Override
public int getItemCount() {
return trackModels.size();
}
public static class ViewHolder extends RecyclerView.ViewHolder {
// creating and initializing variables for text views.
TextView trackNameTV;
TextView trackArtistTV;
public ViewHolder(@NonNull View itemView) {
super(itemView);
trackNameTV = itemView.findViewById(R.id.idTVTrackName);
trackArtistTV = itemView.findViewById(R.id.idTVTrackArtist);
}
}
}
package org.geeksforgeeks.demo
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.recyclerview.widget.RecyclerView
class TrackAdapter(
private val trackModels: ArrayList<TrackModel>, private val context: Context
) :
RecyclerView.Adapter<TrackAdapter.ViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
// inflating layout on below line.
val view: View =
LayoutInflater.from(parent.context).inflate(R.layout.track_rv_item, parent, false)
return ViewHolder(view)
}
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
// setting data to text views.
val trackRVModal = trackModels[position]
holder.trackNameTV.text = trackRVModal.trackName
holder.trackArtistTV.text = trackRVModal.trackArtist
// adding click listener for track item view
holder.itemView.setOnClickListener {
val trackUrl = "https://open.spotify.com/track/" + trackRVModal.id
val uri = Uri.parse(trackUrl) // missing 'http://' will cause crashed
val intent = Intent(Intent.ACTION_VIEW, uri)
context.startActivity(intent)
}
}
override fun getItemCount(): Int {
return trackModels.size
}
inner class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
// creating and initializing variables for text views.
internal val trackNameTV: TextView = itemView.findViewById<TextView>(R.id.idTVTrackName)
val trackArtistTV: TextView = itemView.findViewById<TextView>(R.id.idTVTrackArtist)
}
}
Step 14: Working with SearchActivity
Navigate to SearchActivity and add the following code
package org.geeksforgeeks.demo;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import androidx.recyclerview.widget.RecyclerView;
import com.android.volley.Request;
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
private 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((v, actionId, 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<TrackModel> trackModels = new ArrayList<>();
TrackAdapter trackAdapter = new TrackAdapter(trackModels, this);
songsRV.setAdapter(trackAdapter);
// on below line creating variable for url.
String url = "https://api.spotify.com/v1/search?q=" + searchQuery + "&type=track";
// on below line making json object request
JsonObjectRequest jsonObjectRequest = new JsonObjectRequest(
Request.Method.GET, url, null,
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
trackModels.add(new TrackModel(trackName, trackArtist, trackID));
}
// on below line notifying adapter
trackAdapter.notifyDataSetChanged();
} catch (JSONException e) {
e.printStackTrace();
}
},
error -> Toast.makeText(
SearchActivity.this,
"Fail to get data : " + error, Toast.LENGTH_SHORT
).show()
) {
@Override
public Map<String, String> getHeaders() {
// 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.
Volley.newRequestQueue(this).add(jsonObjectRequest);
}
}
package org.geeksforgeeks.demo
import android.os.Bundle
import android.view.inputmethod.EditorInfo
import android.widget.EditText
import android.widget.TextView.OnEditorActionListener
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.recyclerview.widget.RecyclerView
import com.android.volley.AuthFailureError
import com.android.volley.Response
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import org.json.JSONException
import org.json.JSONObject
class SearchActivity : AppCompatActivity() {
// on below line creating variables
private var searchQuery: String? = ""
private lateinit var searchEdt: EditText
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setContentView(R.layout.activity_search)
// on below line initializing variables.
searchEdt = findViewById(R.id.idEdtSearch)
searchQuery = intent.getStringExtra("searchQuery")
searchEdt.setText(searchQuery)
// on below line adding action listener
// for search edit text
searchEdt.setOnEditorActionListener(OnEditorActionListener { v, actionId, event ->
if (actionId == EditorInfo.IME_ACTION_DONE) {
// on below line calling method to get tracks.
getTracks(searchEdt.getText().toString())
return@OnEditorActionListener true
}
false
})
// on below line getting tracks.
getTracks(searchQuery)
}
private val token: String?
// method to get token.
get() {
val sh = getSharedPreferences("MySharedPref", MODE_PRIVATE)
return sh.getString("token", "Not Found")
}
private fun getTracks(searchQuery: String?) {
// on below line creating and initializing variables
// for recycler view,list and adapter.
val songsRV = findViewById<RecyclerView>(R.id.idRVSongs)
val trackModels = ArrayList<TrackModel>()
val trackAdapter = TrackAdapter(trackModels, this)
songsRV.adapter = trackAdapter
// on below line creating variable for url.
val url = "https://api.spotify.com/v1/search?q=$searchQuery&type=track"
val queue = Volley.newRequestQueue(this@SearchActivity)
// on below line making json object request
val jsonObjectRequest: JsonObjectRequest = object : JsonObjectRequest(
Method.GET, url, null,
Response.Listener<JSONObject> { response ->
try {
val trackObj = response.getJSONObject("tracks")
val itemsArray = trackObj.getJSONArray("items")
for (i in 0 until itemsArray.length()) {
val itemObj = itemsArray.getJSONObject(i)
val trackName = itemObj.getString("name")
val trackArtist =
itemObj.getJSONArray("artists").getJSONObject(0).getString("name")
val trackID = itemObj.getString("id")
// on below line adding data to array list
trackModels.add(TrackModel(trackName, trackArtist, trackID))
}
// on below line notifying adapter
trackAdapter.notifyDataSetChanged()
} catch (e: JSONException) {
e.printStackTrace()
}
},
Response.ErrorListener { error ->
Toast.makeText(
this@SearchActivity,
"Fail to get data : $error", Toast.LENGTH_SHORT
).show()
}) {
@Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
// on below line adding headers.
val headers = HashMap<String, String>()
headers["Authorization"] = token!!
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
return headers
}
}
// adding json object request to queue.
queue.add(jsonObjectRequest)
}
}
Step 15: Create a new Activity (Album Detail Activity)
Create a new activity with the name AlbumDetailActivity. Then, navigate to app > res > layout > activity_album_detail.xml and add the following code
activity_album_detail.xml:
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout
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="match_parent"
android:background="@color/white"
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"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<!-- text view for displaying album name-->
<TextView
android:id="@+id/idTVAlbumName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginTop="16dp"
android:text="Search"
android:textColor="@color/black"
android:textSize="20sp"
android:textStyle="bold"
android:maxLines="1"
app:layout_constraintVertical_chainStyle="packed"
app:layout_constraintBottom_toTopOf="@id/idTVArtistName"
app:layout_constraintEnd_toStartOf="@+id/playButton"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="@+id/playButton" />
<!-- text view for displaying album artist-->
<TextView
android:id="@+id/idTVArtistName"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:maxLines="1"
android:text="Artist Name"
android:textColor="@color/black"
android:textSize="12sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="@+id/playButton"
app:layout_constraintEnd_toStartOf="@+id/playButton"
app:layout_constraintStart_toStartOf="@+id/idTVAlbumName"
app:layout_constraintTop_toBottomOf="@+id/idTVAlbumName" />
<!-- button to play album-->
<ImageView
android:id="@+id/playButton"
android:layout_width="82dp"
android:layout_height="82dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="4dp"
android:src="@drawable/play"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toBottomOf="@+id/idIVAlbum" />
<!-- recycler view to display tracks-->
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/rvAlbumDetails"
android:layout_width="match_parent"
android:layout_height="0dp"
android:layout_margin="4dp"
android:layout_marginTop="32dp"
android:orientation="vertical"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_constraintTop_toBottomOf="@+id/playButton"
tools:layout_editor_absoluteX="4dp"
tools:listitem="@layout/track_rv_item" />
</androidx.constraintlayout.widget.ConstraintLayout>
Design UI:

Step 16: Working with AlbumDetailActivity
Navigate to AlbumDetailActivity and add the following code
package org.geeksforgeeks.demo;
public class AlbumModel {
private String album_type;
private String artistName;
private String external_ids;
private String external_urls;
private String href;
private String id;
private String imageUrl;
private String label;
private String name;
private int popularity;
private String release_date;
private int total_tracks;
private String type;
public AlbumModel(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;
}
// Add getters (and setters if needed)
public String getAlbum_type() { return album_type; }
public String getArtistName() { return artistName; }
public String getExternal_ids() { return external_ids; }
public String getExternal_urls() { return external_urls; }
public String getHref() { return href; }
public String getId() { return id; }
public String getImageUrl() { return imageUrl; }
public String getLabel() { return label; }
public String getName() { return name; }
public int getPopularity() { return popularity; }
public String getRelease_date() { return release_date; }
public int getTotal_tracks() { return total_tracks; }
public String getType() { return type; }
}
package org.geeksforgeeks.demo
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.util.Log
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.Response
import com.android.volley.toolbox.JsonObjectRequest
import com.android.volley.toolbox.Volley
import com.bumptech.glide.Glide
import org.json.JSONException
class AlbumDetailActivity : AppCompatActivity() {
// creating variables on below line.
private var albumID: String? = ""
private var albumImgUrl: String? = null
private var albumName: String? = null
private var artist: String? = null
private var albumUrl: String? = null
private lateinit var albumNameTV: TextView
private lateinit var artistTV: TextView
private lateinit var albumIV: ImageView
private lateinit var playButton: ImageView
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// initializing variables on below line.
setContentView(R.layout.activity_album_detail)
albumID = intent.getStringExtra("id")
albumIV = findViewById(R.id.idIVAlbum)
albumImgUrl = intent.getStringExtra("img")
albumName = intent.getStringExtra("name")
artist = intent.getStringExtra("artist")
albumUrl = intent.getStringExtra("albumUrl")
Log.e("TAG", "album id is : $albumID")
albumNameTV = findViewById(R.id.idTVAlbumName)
playButton = findViewById(R.id.playButton)
artistTV = findViewById(R.id.idTVArtistName)
// setting data on below line.
albumNameTV.text = albumName
artistTV.text = artist
// adding click listener for fab on below line.
playButton.setOnClickListener { // opening album from url on below line.
val uri = Uri.parse(albumUrl) // missing 'http://' will cause crashed
val intent = Intent(Intent.ACTION_VIEW, uri)
startActivity(intent)
}
// loading image on below line.
Glide.with(this).load(albumImgUrl).into(albumIV)
// getting album tracks on below line.
getAlbumTracks(albumID)
}
private val token: String?
// method to get access token.
get() {
val sh = getSharedPreferences("MySharedPref", MODE_PRIVATE)
return sh.getString("token", "Not Found")
}
// method to get tracks from albums.
private fun getAlbumTracks(albumID: String?) {
// on below line creating variable for url
val url = "https://api.spotify.com/v1/albums/$albumID/tracks"
// on below line creating list, initializing adapter and setting it to recycler view.
val trackModels = ArrayList<TrackModel>()
val trackAdapter = TrackAdapter(trackModels, this)
val trackRV = findViewById<RecyclerView>(R.id.rvAlbumDetails)
trackRV.adapter = trackAdapter
val queue = Volley.newRequestQueue(this@AlbumDetailActivity)
// on below line making json object request to parse json data.
val trackObj: JsonObjectRequest = object : JsonObjectRequest(
Method.GET, url, null,
Response.Listener { response ->
try {
val itemsArray = response.getJSONArray("items")
for (i in 0 until itemsArray.length()) {
val itemObj = itemsArray.getJSONObject(i)
val trackName = itemObj.getString("name")
val id = itemObj.getString("id")
val trackArtist =
itemObj.getJSONArray("artists").getJSONObject(0).getString("name")
// on below line adding data to array list.
trackModels.add(TrackModel(trackName, trackArtist, id))
}
trackAdapter.notifyDataSetChanged()
} catch (e: JSONException) {
e.printStackTrace()
}
},
Response.ErrorListener { error ->
Toast.makeText(
this@AlbumDetailActivity,
"Fail to get Tracks$error", Toast.LENGTH_SHORT
).show()
}) {
@Throws(AuthFailureError::class)
override fun getHeaders(): Map<String, String> {
// on below line passing headers.
val headers = HashMap<String, String>()
headers["Authorization"] = token!!
headers["Accept"] = "application/json"
headers["Content-Type"] = "application/json"
return headers
}
}
// on below line adding
// request to queue.
queue.add(trackObj)
}
}
Refer to the following github repo to get the entire code: Spotify-Clone-in-Android
Output: