mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-24 18:04:33 +01:00
Made All Long Running Calls Asynchronous(Coroutines deployed!)
This commit is contained in:
parent
d4823f0e10
commit
735f7c270e
@ -1,6 +1,7 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="shabinder">
|
||||
<words>
|
||||
<w>moshi</w>
|
||||
<w>musicforeveryone</w>
|
||||
<w>musicplaceholder</w>
|
||||
<w>shabinder</w>
|
||||
|
@ -1,5 +1,8 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ProjectPlainTextFileTypeManager">
|
||||
<file url="file://$PROJECT_DIR$/app/src/main/java/com/shabinder/musicForEveryone/downloadHelper/DownloadHelperLibrary.kt" />
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_8" project-jdk-name="1.8" project-jdk-type="JavaSDK">
|
||||
<output url="file://$PROJECT_DIR$/build/classes" />
|
||||
</component>
|
||||
|
@ -68,9 +68,14 @@ dependencies {
|
||||
implementation 'com.google.apis:google-api-services-youtube:v3-rev180-1.22.0'
|
||||
implementation 'com.google.oauth-client:google-oauth-client:1.22.0'
|
||||
implementation 'com.spotify.android:auth:1.1.0'
|
||||
implementation 'com.google.code.gson:gson:2.8.6'
|
||||
implementation 'com.squareup.okhttp3:okhttp:4.8.0'
|
||||
|
||||
implementation 'com.squareup.retrofit2:retrofit:2.9.0'
|
||||
implementation "com.squareup.moshi:moshi:1.9.3"
|
||||
implementation "com.squareup.moshi:moshi-kotlin:1.9.3"
|
||||
implementation "com.squareup.retrofit2:converter-moshi:2.9.0"
|
||||
|
||||
|
||||
implementation 'com.github.sealedtx:java-youtube-downloader:2.2.2'
|
||||
|
||||
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
|
||||
|
@ -21,6 +21,11 @@
|
||||
android:theme="@style/AppTheme">
|
||||
|
||||
<activity android:name="com.shabinder.musicForEveryone.MainActivity">
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:mimeType="text/plain" />
|
||||
</intent-filter>
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
|
||||
|
@ -6,43 +6,52 @@ import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.StrictMode
|
||||
import android.util.Log
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.shabinder.musicForEveryone.databinding.MainActivityBinding
|
||||
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper
|
||||
import com.shabinder.musicForEveryone.utils.SpotifyNewService
|
||||
import com.shabinder.musicForEveryone.utils.YoutubeInterface
|
||||
import com.spotify.sdk.android.authentication.AuthenticationClient
|
||||
import com.spotify.sdk.android.authentication.AuthenticationRequest
|
||||
import com.spotify.sdk.android.authentication.AuthenticationResponse
|
||||
import com.spotify.sdk.android.authentication.LoginActivity
|
||||
import com.squareup.moshi.Moshi
|
||||
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
|
||||
import kaaes.spotify.webapi.android.SpotifyApi
|
||||
import kaaes.spotify.webapi.android.SpotifyService
|
||||
import retrofit.RestAdapter
|
||||
import kotlinx.coroutines.launch
|
||||
import okhttp3.Interceptor
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.Response
|
||||
import retrofit2.Retrofit
|
||||
import retrofit2.converter.moshi.MoshiConverterFactory
|
||||
|
||||
|
||||
class MainActivity : AppCompatActivity() {
|
||||
class MainActivity : AppCompatActivity() ,DownloadHelper{
|
||||
private lateinit var binding: MainActivityBinding
|
||||
var ytDownloader : YoutubeDownloader? = null
|
||||
var spotifyExtra : SpotifyNewService? = null
|
||||
var downloadManager : DownloadManager? = null
|
||||
val REDIRECT_URI = "musicforeveryone://callback"
|
||||
val CLIENT_ID:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
|
||||
// val musicDirectory = File(this.filesDir?.absolutePath + "/Music/")
|
||||
var message :String =""
|
||||
var token :String =""
|
||||
var spotify: SpotifyService? = null
|
||||
lateinit var sharedViewModel: SharedViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = DataBindingUtil.setContentView(this,R.layout.main_activity)
|
||||
|
||||
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
|
||||
val policy =
|
||||
StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
StrictMode.setThreadPolicy(policy)
|
||||
// val policy =
|
||||
// StrictMode.ThreadPolicy.Builder().permitAll().build()
|
||||
// StrictMode.setThreadPolicy(policy)
|
||||
//TODO Use Coroutines
|
||||
if(spotify==null){
|
||||
authenticateSpotify()
|
||||
@ -59,7 +68,14 @@ class MainActivity : AppCompatActivity() {
|
||||
downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
|
||||
sharedViewModel.downloadManager = downloadManager
|
||||
|
||||
|
||||
if (intent?.action == Intent.ACTION_SEND) {
|
||||
if ("text/plain" == intent.type) {
|
||||
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
||||
Log.i("Intent Received",it)
|
||||
sharedViewModel.intentString = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -85,13 +101,21 @@ class MainActivity : AppCompatActivity() {
|
||||
spotify = api.service
|
||||
sharedViewModel.spotify = api.service
|
||||
//Initiate Processes In Main Fragment
|
||||
val me = spotify?.me?.display_name
|
||||
sharedViewModel.userName.value = me
|
||||
Log.i("Network",me!!)
|
||||
|
||||
sharedViewModel.uiScope.launch {
|
||||
val me = spotifyExtra?.getMe()?.display_name
|
||||
sharedViewModel.userName.value = me
|
||||
Log.i("Network",me!!)
|
||||
}
|
||||
|
||||
sharedViewModel.userName.observe(this, Observer {
|
||||
binding.message.text = it
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
AuthenticationResponse.Type.ERROR -> {
|
||||
Log.i("Network",response.error.toString())
|
||||
|
||||
}
|
||||
else -> {
|
||||
}
|
||||
@ -103,17 +127,33 @@ class MainActivity : AppCompatActivity() {
|
||||
* Adding my own new Spotify Web Api Requests!
|
||||
* */
|
||||
private fun implementSpotifyExtra() {
|
||||
val restAdapter = RestAdapter.Builder()
|
||||
.setEndpoint(SpotifyApi.SPOTIFY_WEB_API_ENDPOINT)
|
||||
.setRequestInterceptor { request ->
|
||||
request.addHeader(
|
||||
"Authorization",
|
||||
"Bearer $token"
|
||||
)
|
||||
|
||||
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
|
||||
|
||||
httpClient.addInterceptor(object : Interceptor {
|
||||
override fun intercept(chain: Interceptor.Chain): Response {
|
||||
val request: Request =
|
||||
chain.request().newBuilder().addHeader(
|
||||
"Authorization",
|
||||
"Bearer $token"
|
||||
).build()
|
||||
return chain.proceed(request)
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
val moshi = Moshi.Builder()
|
||||
.add(KotlinJsonAdapterFactory())
|
||||
.build()
|
||||
|
||||
val spotifyExtra = restAdapter.create(SpotifyNewService::class.java)
|
||||
val retrofit: Retrofit =
|
||||
Retrofit.Builder()
|
||||
.baseUrl("https://api.spotify.com/v1/")
|
||||
.client(httpClient.build())
|
||||
.addConverterFactory(MoshiConverterFactory.create(moshi))
|
||||
.build()
|
||||
|
||||
spotifyExtra = retrofit.create(SpotifyNewService::class.java)
|
||||
sharedViewModel.spotifyExtra = spotifyExtra
|
||||
}
|
||||
|
||||
@ -129,7 +169,7 @@ class MainActivity : AppCompatActivity() {
|
||||
|
||||
private fun authenticateSpotify() {
|
||||
val builder = AuthenticationRequest.Builder(CLIENT_ID,AuthenticationResponse.Type.TOKEN,REDIRECT_URI)
|
||||
.setShowDialog(true)
|
||||
.setShowDialog(false)
|
||||
.setScopes(arrayOf("user-read-private","streaming","user-read-email","user-modify-playback-state","user-top-read","user-library-modify","user-read-currently-playing","user-library-read","user-read-recently-played"))
|
||||
val request: AuthenticationRequest = builder.build()
|
||||
AuthenticationClient.openLoginActivity(this, LoginActivity.REQUEST_CODE, request)
|
||||
|
@ -9,8 +9,12 @@ import kaaes.spotify.webapi.android.SpotifyService
|
||||
import kaaes.spotify.webapi.android.models.Album
|
||||
import kaaes.spotify.webapi.android.models.Playlist
|
||||
import kaaes.spotify.webapi.android.models.Track
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
class SharedViewModel : ViewModel() {
|
||||
var intentString = ""
|
||||
var accessToken:String = ""
|
||||
var userName = MutableLiveData<String>().apply { value = "Placeholder" }
|
||||
var spotify :SpotifyService? = null
|
||||
@ -18,15 +22,23 @@ class SharedViewModel : ViewModel() {
|
||||
var ytDownloader : YoutubeDownloader? = null
|
||||
var downloadManager : DownloadManager? = null
|
||||
|
||||
var viewModelJob = Job()
|
||||
|
||||
fun getTrackDetails(trackLink:String): Track?{
|
||||
return spotify?.getTrack(trackLink)
|
||||
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
|
||||
|
||||
suspend fun getTrackDetails(trackLink:String): Track?{
|
||||
return spotifyExtra?.getTrack(trackLink)
|
||||
}
|
||||
fun getAlbumDetails(albumLink:String): Album?{
|
||||
return spotify?.getAlbum(albumLink)
|
||||
suspend fun getAlbumDetails(albumLink:String): Album?{
|
||||
return spotifyExtra?.getAlbum(albumLink)
|
||||
}
|
||||
fun getPlaylistDetails(link:String): Playlist?{
|
||||
suspend fun getPlaylistDetails(link:String): Playlist?{
|
||||
return spotifyExtra?.getPlaylist(link)
|
||||
}
|
||||
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
viewModelJob.cancel()
|
||||
}
|
||||
}
|
@ -9,51 +9,62 @@ import com.github.kiulian.downloader.YoutubeDownloader
|
||||
import com.github.kiulian.downloader.model.formats.Format
|
||||
import com.github.kiulian.downloader.model.quality.AudioQuality
|
||||
import com.shabinder.musicForEveryone.utils.YoutubeInterface
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
interface DownloadHelper {
|
||||
fun downloadTrack(ytDownloader: YoutubeDownloader?, downloadManager: DownloadManager?, searchQuery:String){
|
||||
//@data = 1st object from YT query.
|
||||
val data = YoutubeInterface.search(searchQuery)?.get(0)
|
||||
if (data==null){Log.i("DownloadHelper","Youtube Request Failed!")}else{
|
||||
suspend fun downloadTrack(ytDownloader: YoutubeDownloader?, downloadManager: DownloadManager?, searchQuery:String){
|
||||
|
||||
//Fetching a Video Object.
|
||||
val video = ytDownloader?.getVideo(data.id)
|
||||
val details = video?.details()
|
||||
withContext(Dispatchers.IO){
|
||||
val downloadIdList = mutableListOf<Int>()
|
||||
val data = YoutubeInterface.search(searchQuery)?.get(0)
|
||||
if (data==null){Log.i("DownloadHelper","Youtube Request Failed!")}else{
|
||||
|
||||
val format:Format = video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format
|
||||
val video = ytDownloader?.getVideo(data.id)
|
||||
//Fetching a Video Object.
|
||||
val details = video?.details()
|
||||
|
||||
val audioUrl = format.url()
|
||||
val format:Format = video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format
|
||||
|
||||
val audioUrl = format.url()
|
||||
|
||||
if (audioUrl != null) {
|
||||
downloadFile(audioUrl,downloadManager,details!!.title())
|
||||
Log.i("DHelper Start Download", audioUrl)
|
||||
}else{Log.i("YT audio url is null", format.toString())}
|
||||
|
||||
}
|
||||
|
||||
if (audioUrl != null) {
|
||||
downloadFile(audioUrl,downloadManager,details?.title()?:"Error")
|
||||
Log.i("DHelper Start Download", audioUrl)
|
||||
}else{Log.i("YT audio url is null", format.toString())}
|
||||
|
||||
// Library Inbuilt function to Save File (Need Scoped Storage Implementation)
|
||||
// val file: File = video.download( format , outputDir)
|
||||
}
|
||||
}
|
||||
//@data = 1st object from YT query.
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Downloading Using Android Download Manager
|
||||
* */
|
||||
fun downloadFile(url: String, downloadManager: DownloadManager?,title:String){
|
||||
val audioUri = Uri.parse(url)
|
||||
val outputDir = File.separator + "Spotify-Downloads" +File.separator + "${removeIllegalChars(title)}.mp3"
|
||||
suspend fun downloadFile(url: String, downloadManager: DownloadManager?,title:String){
|
||||
withContext(Dispatchers.IO){
|
||||
val audioUri = Uri.parse(url)
|
||||
val outputDir = File.separator + "Spotify-Downloads" +File.separator + "${removeIllegalChars(title)}.mp3"
|
||||
|
||||
val request = DownloadManager.Request(audioUri)
|
||||
.setAllowedNetworkTypes(
|
||||
DownloadManager.Request.NETWORK_WIFI or
|
||||
DownloadManager.Request.NETWORK_MOBILE
|
||||
)
|
||||
.setAllowedOverRoaming(false)
|
||||
.setTitle(title)
|
||||
.setDescription("Spotify Downloader Working Up here...")
|
||||
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC,outputDir)
|
||||
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
downloadManager?.enqueue(request)
|
||||
Log.i("DownloadManager","Download Request Sent")
|
||||
val request = DownloadManager.Request(audioUri)
|
||||
.setAllowedNetworkTypes(
|
||||
DownloadManager.Request.NETWORK_WIFI or
|
||||
DownloadManager.Request.NETWORK_MOBILE
|
||||
)
|
||||
.setAllowedOverRoaming(false)
|
||||
.setTitle(title)
|
||||
.setDescription("Spotify Downloader Working Up here...")
|
||||
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC,outputDir)
|
||||
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
|
||||
downloadManager?.enqueue(request)
|
||||
Log.i("DownloadManager","Download Request Sent")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -18,6 +18,7 @@ import com.shabinder.musicForEveryone.recyclerView.TrackListAdapter
|
||||
import com.shabinder.musicForEveryone.utils.bindImage
|
||||
import kaaes.spotify.webapi.android.SpotifyService
|
||||
import kaaes.spotify.webapi.android.models.Track
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
|
||||
class MainFragment : Fragment(),DownloadHelper {
|
||||
@ -25,6 +26,7 @@ class MainFragment : Fragment(),DownloadHelper {
|
||||
private lateinit var sharedViewModel: SharedViewModel
|
||||
var spotify : SpotifyService? = null
|
||||
var type:String = ""
|
||||
var spotifyLink = ""
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater, container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
@ -33,13 +35,9 @@ class MainFragment : Fragment(),DownloadHelper {
|
||||
|
||||
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
|
||||
spotify = sharedViewModel.spotify
|
||||
sharedViewModel.userName.observe(viewLifecycleOwner, Observer {
|
||||
binding.message.text = it
|
||||
// if(it!="Placeholder"){Snackbar.make(requireView(),"Hello, $it!", Snackbar.LENGTH_SHORT).show()}
|
||||
})
|
||||
|
||||
binding.btnSearch.setOnClickListener {
|
||||
val spotifyLink = binding.spotifyLink.text.toString()
|
||||
spotifyLink = binding.spotifyLink.text.toString()
|
||||
|
||||
val link = spotifyLink.substringAfterLast('/' , "Error").substringBefore('?')
|
||||
type = spotifyLink.substringBeforeLast('/' , "Error").substringAfterLast('/')
|
||||
@ -49,60 +47,83 @@ class MainFragment : Fragment(),DownloadHelper {
|
||||
val adapter = TrackListAdapter()
|
||||
binding.trackList.adapter = adapter
|
||||
adapter.sharedViewModel = sharedViewModel
|
||||
|
||||
when(type){
|
||||
"track" -> {
|
||||
val trackObject = sharedViewModel.getTrackDetails(link)
|
||||
sharedViewModel.uiScope.launch{
|
||||
val trackObject = sharedViewModel.getTrackDetails(link)
|
||||
|
||||
val trackList = mutableListOf<Track>()
|
||||
trackList.add(trackObject!!)
|
||||
adapter.totalItems = 1
|
||||
adapter.trackList = trackList
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
val trackList = mutableListOf<Track>()
|
||||
trackList.add(trackObject!!)
|
||||
bindImage(binding.imageView, trackObject.album.images[0].url)
|
||||
adapter.totalItems = 1
|
||||
adapter.trackList = trackList
|
||||
adapter.notifyDataSetChanged()
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
val albumObject = sharedViewModel.getAlbumDetails(link)
|
||||
sharedViewModel.uiScope.launch{
|
||||
|
||||
bindImage(binding.imageView,albumObject!!.images[1].url)
|
||||
binding.titleView.text = albumObject.name
|
||||
binding.titleView.visibility =View.VISIBLE
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
val albumObject = sharedViewModel.getAlbumDetails(link)
|
||||
|
||||
val trackList = mutableListOf<Track>()
|
||||
albumObject.tracks?.items?.forEach { trackList.add(it as Track) }
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.trackList = trackList
|
||||
adapter.notifyDataSetChanged()
|
||||
binding.titleView.text = albumObject!!.name
|
||||
binding.titleView.visibility =View.VISIBLE
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
|
||||
val trackList = mutableListOf<Track>()
|
||||
albumObject.tracks?.items?.forEach { trackList.add(it as Track) }
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.trackList = trackList
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
|
||||
bindImage(binding.imageView, albumObject.images[0].url)
|
||||
}
|
||||
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
val playlistObject = sharedViewModel.getPlaylistDetails(link)
|
||||
sharedViewModel.uiScope.launch{
|
||||
val playlistObject = sharedViewModel.getPlaylistDetails(link)
|
||||
|
||||
bindImage(binding.imageView,playlistObject!!.images[0].url)
|
||||
binding.titleView.text = playlistObject.name
|
||||
binding.titleView.visibility =View.VISIBLE
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
binding.titleView.text = "${if(playlistObject!!.name.length > 18){"${playlistObject.name.subSequence(0,17)}..."}else{playlistObject.name}}"
|
||||
binding.titleView.visibility =View.VISIBLE
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
binding.playlistOwner.visibility =View.VISIBLE
|
||||
binding.playlistOwner.text = "by: ${playlistObject.owner.display_name}"
|
||||
val trackList = mutableListOf<Track>()
|
||||
playlistObject.tracks?.items!!.forEach { trackList.add(it.track) }
|
||||
adapter.trackList = trackList.toList()
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
|
||||
|
||||
bindImage(binding.imageView, playlistObject.images[0].url)
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
val trackList = mutableListOf<Track>()
|
||||
playlistObject.tracks?.items!!.forEach { trackList.add(it.track) }
|
||||
adapter.trackList = trackList.toList()
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.notifyDataSetChanged()
|
||||
Log.i("Adapter",trackList.size.toString())
|
||||
}
|
||||
|
||||
"episode" -> {showToast("Implementation Pending")}
|
||||
"show" -> {showToast("Implementation Pending ")}
|
||||
}
|
||||
|
||||
binding.spotifyLink.setText(link)
|
||||
}
|
||||
binding.spotifyLink.setText(sharedViewModel.intentString)
|
||||
sharedViewModel.userName.observe(viewLifecycleOwner, Observer {
|
||||
//Waiting for Authentication to Finish with Spotify
|
||||
if (it != "Placeholder"){
|
||||
if(sharedViewModel.intentString != ""){binding.btnSearch.performClick()}
|
||||
}
|
||||
})
|
||||
|
||||
return binding.root
|
||||
}
|
||||
|
||||
|
@ -3,7 +3,7 @@ package com.shabinder.musicForEveryone.recyclerView
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -12,6 +12,7 @@ import com.shabinder.musicForEveryone.SharedViewModel
|
||||
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper
|
||||
import com.shabinder.musicForEveryone.utils.bindImage
|
||||
import kaaes.spotify.webapi.android.models.Track
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),DownloadHelper {
|
||||
|
||||
@ -30,25 +31,25 @@ class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),Downl
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
val item = trackList[position]
|
||||
if(totalItems == 1){holder.coverImage.visibility = View.GONE}else{
|
||||
bindImage(holder.coverImage,item.album.images[0].url)
|
||||
}
|
||||
|
||||
bindImage(holder.coverImage,item.album.images[0].url)
|
||||
|
||||
holder.trackName.text = "${if(item.name.length > 22){"${item.name.subSequence(0,19)}..."}else{item.name}}"
|
||||
holder.trackName.text = "${if(item.name.length > 17){"${item.name.subSequence(0,16)}..."}else{item.name}}"
|
||||
holder.artistName.text = "${item.artists[0]?.name?:""}..."
|
||||
holder.popularity.text = item.popularity.toString()
|
||||
holder.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} seconds"
|
||||
holder.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
|
||||
holder.downloadBtn.setOnClickListener{
|
||||
downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${item.name} ${item.artists[0].name?:""}")
|
||||
sharedViewModel.uiScope.launch {
|
||||
downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${item.name} ${item.artists[0].name?:""}")
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView){
|
||||
val trackName:TextView = itemView.findViewById(R.id.track_name)
|
||||
val artistName:TextView = itemView.findViewById(R.id.artist)
|
||||
val popularity:TextView = itemView.findViewById(R.id.popularity)
|
||||
val duration:TextView = itemView.findViewById(R.id.duration)
|
||||
val downloadBtn:Button = itemView.findViewById(R.id.btn_download)
|
||||
val downloadBtn:ImageButton = itemView.findViewById(R.id.btn_download)
|
||||
val coverImage:ImageView = itemView.findViewById(R.id.imageUrl)
|
||||
|
||||
}
|
||||
}
|
||||
}
|
@ -10,12 +10,14 @@ import com.shabinder.musicForEveryone.R
|
||||
|
||||
@BindingAdapter("imageUrl")
|
||||
fun bindImage(imgView: ImageView, imgUrl: String?) {
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
Glide.with(imgView.context)
|
||||
.load(imgUri)
|
||||
.apply(RequestOptions()
|
||||
.error(R.drawable.ic_musicplaceholder))
|
||||
.into(imgView)
|
||||
}
|
||||
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
Glide.with(imgView.context)
|
||||
.load(imgUri)
|
||||
.apply(RequestOptions()
|
||||
.error(R.drawable.ic_musicplaceholder))
|
||||
.into(imgView)
|
||||
}
|
||||
|
||||
}
|
@ -1,15 +1,36 @@
|
||||
package com.shabinder.musicForEveryone.utils
|
||||
|
||||
import kaaes.spotify.webapi.android.models.Playlist
|
||||
import retrofit.http.GET
|
||||
import retrofit.http.Path
|
||||
import kaaes.spotify.webapi.android.models.*
|
||||
import retrofit2.http.GET
|
||||
import retrofit2.http.Path
|
||||
|
||||
|
||||
interface SpotifyNewService {
|
||||
|
||||
@GET("/playlists/{playlist_id}")
|
||||
fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist?
|
||||
@GET("playlists/{playlist_id}")
|
||||
suspend fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist?
|
||||
|
||||
@GET("tracks/{id}")
|
||||
suspend fun getTrack(@Path("id") var1: String?): Track?
|
||||
|
||||
@GET("albums/{id}")
|
||||
suspend fun getAlbum(@Path("id") var1: String?): Album?
|
||||
|
||||
@GET("me")
|
||||
suspend fun getMe(): com.shabinder.musicForEveryone.utils.UserPrivate?
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
data class UserPrivate(
|
||||
val country:String,
|
||||
var display_name: String,
|
||||
val email:String,
|
||||
var external_urls: Map<String?, String?>? = null,
|
||||
var followers: Followers? = null,
|
||||
var href: String? = null,
|
||||
var id: String? = null,
|
||||
var images: List<Image?>? = null,
|
||||
var product:String,
|
||||
var type: String? = null,
|
||||
var uri: String? = null)
|
@ -7,13 +7,32 @@
|
||||
android:id="@+id/mainActivity"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="2dp"
|
||||
android:background="@drawable/text_background"
|
||||
android:padding="5dp"
|
||||
android:paddingTop="6dp"
|
||||
android:text="MainFragment"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<fragment
|
||||
android:id="@+id/NavHostFragment"
|
||||
android:name="androidx.navigation.fragment.NavHostFragment"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_height="0dp"
|
||||
app:defaultNavHost="true"
|
||||
app:layout_constraintBottom_toTopOf="@+id/message"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:navGraph="@navigation/navigation"
|
||||
tools:ignore="FragmentTagUsage" />
|
||||
|
||||
|
@ -1,105 +1,128 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools">
|
||||
<androidx.coordinatorlayout.widget.CoordinatorLayout
|
||||
android:id="@+id/main"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="25dp"
|
||||
android:fitsSystemWindows="true"
|
||||
tools:context=".fragments.MainFragment">
|
||||
|
||||
<com.google.android.material.appbar.AppBarLayout
|
||||
android:id="@+id/appbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="230dp">
|
||||
|
||||
<com.google.android.material.appbar.CollapsingToolbarLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
app:contentScrim="?attr/colorPrimary"
|
||||
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
|
||||
app:layout_scrollInterpolator="@android:anim/decelerate_interpolator"
|
||||
app:toolbarId="@+id/toolbar">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<EditText
|
||||
android:id="@+id/spotifyLink"
|
||||
android:layout_width="257dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="8dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginEnd="20dp"
|
||||
android:background="@drawable/text_background"
|
||||
android:ems="10"
|
||||
android:hint="Link From Spotify"
|
||||
android:inputType="text"
|
||||
android:padding="5dp"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHint="@color/grey"
|
||||
android:textSize="19sp" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_search"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:text="Search"
|
||||
android:textSize="18sp" />
|
||||
</LinearLayout>
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:contentDescription="Album Cover"
|
||||
android:scaleType="centerInside"
|
||||
android:src="@drawable/ic_launcher_foreground"
|
||||
app:layout_collapseMode="parallax"/>
|
||||
</LinearLayout>
|
||||
</com.google.android.material.appbar.CollapsingToolbarLayout>
|
||||
|
||||
</com.google.android.material.appbar.AppBarLayout>
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:id="@+id/main"
|
||||
android:id="@+id/constraint_layout"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
tools:context=".fragments.MainFragment">
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/message"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="8dp"
|
||||
android:text="MainFragment"
|
||||
android:background="@drawable/text_backdround"
|
||||
android:padding="5dp"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<EditText
|
||||
android:id="@+id/spotifyLink"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="25dp"
|
||||
android:background="@drawable/text_backdround"
|
||||
android:hint="Link From Spotify"
|
||||
android:ems="10"
|
||||
android:padding="5dp"
|
||||
android:inputType="text"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/white"
|
||||
android:textColorHint="@color/grey"
|
||||
android:textSize="20dp"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_search"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/btn_search"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:backgroundTint="@color/colorPrimary"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="Search"
|
||||
android:textSize="18sp"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/spotifyLink"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/spotifyLink"
|
||||
app:layout_constraintTop_toTopOf="@+id/spotifyLink" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/track_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="3dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_constraintBottom_toTopOf="@+id/message"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_view"
|
||||
app:layout_constraintVertical_bias="0.0" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/image_view"
|
||||
android:layout_width="150dp"
|
||||
android:layout_height="150dp"
|
||||
android:layout_marginTop="8dp"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/spotifyLink"
|
||||
android:visibility="gone"/>
|
||||
|
||||
<TextView
|
||||
android:id="@+id/title_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="2dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:text="TextView"
|
||||
android:background="@drawable/text_backdround"
|
||||
android:drawableTint="@color/colorPrimary"
|
||||
android:paddingLeft="6dp"
|
||||
android:paddingRight="6dp"
|
||||
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
|
||||
android:textAllCaps="false"
|
||||
android:layout_marginTop="10dp"
|
||||
android:text="Shabinder's Playlist"
|
||||
android:textAlignment="center"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="24sp"
|
||||
android:textSize="26sp"
|
||||
android:visibility="gone"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/track_list"
|
||||
app:layout_constraintEnd_toStartOf="@+id/playlist_owner"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/playlist_owner"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@drawable/text_background_accented"
|
||||
android:paddingLeft="8dp"
|
||||
android:paddingRight="8dp"
|
||||
android:text="by : Shabinder"
|
||||
android:textColor="@color/white"
|
||||
android:textSize="15sp"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/title_view"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/title_view" />
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/track_list"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:layout_marginTop="12dp"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/image_view" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/title_view" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
|
||||
</androidx.coordinatorlayout.widget.CoordinatorLayout>
|
||||
</layout>
|
||||
|
@ -6,130 +6,80 @@
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_height="80dp"
|
||||
android:background="#000000"
|
||||
android:layout_marginBottom="12dp"
|
||||
>
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/imageUrl"
|
||||
android:layout_width="100dp"
|
||||
android:layout_height="100dp"
|
||||
android:layout_height="80dp"
|
||||
android:contentDescription="Track Image"
|
||||
android:visibility="visible"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/artist"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_musicplaceholder"
|
||||
tools:tint="#A3FFFFFF"/>
|
||||
tools:tint="#A3FFFFFF" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/track_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:fontFamily="monospace"
|
||||
android:text="TextView"
|
||||
android:letterSpacing="-0.03"
|
||||
android:lines="1"
|
||||
android:text="The Spectre"
|
||||
android:textAllCaps="false"
|
||||
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
|
||||
android:textColor="@color/colorPrimary"
|
||||
android:textSize="20sp"
|
||||
android:textSize="18sp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageUrl"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_download"
|
||||
app:layout_constraintStart_toStartOf="@+id/artist"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="4dp"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="Artist"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textview3"
|
||||
app:layout_constraintEnd_toStartOf="@+id/artist"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageUrl"
|
||||
app:layout_constraintTop_toBottomOf="@+id/track_name" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/artist"
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="12dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/textView1"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/textView1"
|
||||
app:layout_constraintTop_toTopOf="@+id/textView1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textview3"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="Popularity"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/textView5"
|
||||
app:layout_constraintEnd_toStartOf="@+id/popularity"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageUrl"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textView1" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/popularity"
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/textview3"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/textview3"
|
||||
app:layout_constraintTop_toTopOf="@+id/textview3" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/textView5"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginBottom="4dp"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="Duration"
|
||||
android:textSize="12dp"
|
||||
android:textStyle="bold"
|
||||
app:layout_constraintBottom_toTopOf="@+id/btn_download"
|
||||
android:text="Alan Walker"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@+id/duration"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/imageUrl"
|
||||
app:layout_constraintTop_toBottomOf="@+id/textview3" />
|
||||
app:layout_constraintTop_toBottomOf="@+id/track_name" />
|
||||
|
||||
|
||||
<TextView
|
||||
android:id="@+id/duration"
|
||||
style="@style/TextAppearance.AppCompat.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:paddingLeft="9dp"
|
||||
android:text="TextView"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/textView5"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0.5"
|
||||
app:layout_constraintStart_toEndOf="@+id/textView5"
|
||||
app:layout_constraintTop_toTopOf="@+id/textView5" />
|
||||
android:text="4 minutes, 20 sec"
|
||||
app:layout_constraintBottom_toBottomOf="@+id/artist"
|
||||
app:layout_constraintEnd_toStartOf="@+id/btn_download"
|
||||
app:layout_constraintStart_toEndOf="@+id/artist"
|
||||
app:layout_constraintTop_toTopOf="@+id/artist" />
|
||||
|
||||
<Button
|
||||
<ImageButton
|
||||
android:id="@+id/btn_download"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:backgroundTint="#27FF3D"
|
||||
android:text="Download"
|
||||
android:textSize="14sp"
|
||||
android:layout_width="60dp"
|
||||
android:layout_height="0dp"
|
||||
android:backgroundTint="@color/black"
|
||||
android:scaleType="fitCenter"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="1.0"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@+id/imageUrl" />
|
||||
app:layout_constraintTop_toTopOf="parent"
|
||||
app:srcCompat="@drawable/ic_arrow" />
|
||||
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
Loading…
Reference in New Issue
Block a user