Repo refreshed and cleaned,Basic YT Functionality Added(TODO Coroutines!)

This commit is contained in:
shabinder 2020-07-19 18:31:29 +05:30
parent 3cb3162beb
commit 8fcb22ab5d
14 changed files with 483 additions and 42 deletions

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<project version="4"> <project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_1_7" project-jdk-name="1.8" project-jdk-type="JavaSDK"> <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" /> <output url="file://$PROJECT_DIR$/build/classes" />
</component> </component>
<component name="ProjectType"> <component name="ProjectType">

View File

@ -30,6 +30,13 @@ android {
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
} }
} }
compileOptions {
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_1_8
}
kotlinOptions {
jvmTarget = JavaVersion.VERSION_1_8.toString()
}
} }
dependencies { dependencies {
@ -51,6 +58,8 @@ dependencies {
implementation "androidx.room:room-runtime:2.2.5" implementation "androidx.room:room-runtime:2.2.5"
kapt "androidx.room:room-compiler:2.2.5" kapt "androidx.room:room-compiler:2.2.5"
implementation "androidx.room:room-ktx:2.2.5" implementation "androidx.room:room-ktx:2.2.5"
implementation "com.github.bumptech.glide:glide:4.11.0"
kapt "com.github.bumptech.glide:compiler:4.11.0"
implementation 'androidx.recyclerview:recyclerview:1.1.0' implementation 'androidx.recyclerview:recyclerview:1.1.0'
@ -62,6 +71,8 @@ dependencies {
implementation 'com.google.code.gson:gson:2.8.6' implementation 'com.google.code.gson:gson:2.8.6'
implementation 'com.squareup.okhttp3:okhttp:4.8.0' implementation 'com.squareup.okhttp3:okhttp:4.8.0'
implementation 'com.github.sealedtx:java-youtube-downloader:2.2.1'
implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava' implementation 'com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava'
testImplementation 'junit:junit:4.13' testImplementation 'junit:junit:4.13'
androidTestImplementation 'androidx.test.ext:junit:1.1.1' androidTestImplementation 'androidx.test.ext:junit:1.1.1'

View File

@ -3,6 +3,7 @@
package="com.shabinder.musicForEveryone"> package="com.shabinder.musicForEveryone">
<uses-permission android:name="android.permission.INTERNET"/> <uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" /> <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" /> <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />

View File

@ -0,0 +1,20 @@
package com.shabinder.musicForEveryone
import android.widget.ImageView
import androidx.core.net.toUri
import androidx.databinding.BindingAdapter
import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
@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)
}
}

View File

@ -9,29 +9,33 @@ import android.util.Log
import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.app.AppCompatActivity
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.musicForEveryone.databinding.MainActivityBinding import com.shabinder.musicForEveryone.databinding.MainActivityBinding
import com.shabinder.musicForEveryone.utils.YoutubeConnector
import com.spotify.sdk.android.authentication.AuthenticationClient import com.spotify.sdk.android.authentication.AuthenticationClient
import com.spotify.sdk.android.authentication.AuthenticationRequest import com.spotify.sdk.android.authentication.AuthenticationRequest
import com.spotify.sdk.android.authentication.AuthenticationResponse import com.spotify.sdk.android.authentication.AuthenticationResponse
import com.spotify.sdk.android.authentication.LoginActivity import com.spotify.sdk.android.authentication.LoginActivity
import kaaes.spotify.webapi.android.SpotifyApi import kaaes.spotify.webapi.android.SpotifyApi
import kaaes.spotify.webapi.android.SpotifyService import kaaes.spotify.webapi.android.SpotifyService
import retrofit.RestAdapter
class MainActivity : AppCompatActivity() { class MainActivity : AppCompatActivity() {
private lateinit var binding: MainActivityBinding private lateinit var binding: MainActivityBinding
var ytDownloader : YoutubeDownloader? = null
val REDIRECT_URI = "musicforeveryone://callback" val REDIRECT_URI = "musicforeveryone://callback"
val CLIENT_ID:String = "694d8bf4f6ec420fa66ea7fb4c68f89d" val CLIENT_ID:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
// val musicDirectory = File(this.filesDir?.absolutePath + "/Music/") // val musicDirectory = File(this.filesDir?.absolutePath + "/Music/")
var message :String ="" var message :String =""
var token :String ="" var token :String =""
var spotify: SpotifyService? = null var spotify: SpotifyService? = null
lateinit var mainViewModel: MainViewModel lateinit var sharedViewModel: SharedViewModel
override fun onCreate(savedInstanceState: Bundle?) { override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
binding = DataBindingUtil.setContentView(this,R.layout.main_activity) binding = DataBindingUtil.setContentView(this,R.layout.main_activity)
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java) sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
val policy = val policy =
StrictMode.ThreadPolicy.Builder().permitAll().build() StrictMode.ThreadPolicy.Builder().permitAll().build()
StrictMode.setThreadPolicy(policy) StrictMode.setThreadPolicy(policy)
@ -41,15 +45,16 @@ class MainActivity : AppCompatActivity() {
} }
requestPermission() requestPermission()
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
ytDownloader = YoutubeDownloader()
sharedViewModel.ytDownloader = ytDownloader
//Initialing Communication with Youtube
YoutubeConnector.youtubeConnector()
} }
private fun authenticateSpotify() {
val builder = AuthenticationRequest.Builder(CLIENT_ID,AuthenticationResponse.Type.TOKEN,REDIRECT_URI)
.setShowDialog(true)
.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)
}
override fun onActivityResult( override fun onActivityResult(
requestCode: Int, requestCode: Int,
@ -64,18 +69,18 @@ class MainActivity : AppCompatActivity() {
AuthenticationResponse.Type.TOKEN -> { AuthenticationResponse.Type.TOKEN -> {
Log.i("Network",response.accessToken.toString()) Log.i("Network",response.accessToken.toString())
token = response.accessToken token = response.accessToken
sharedViewModel.accessToken = response.accessToken
//Implementing My Own Spotify Requests
implementSpotifyExtra()
val api = SpotifyApi() val api = SpotifyApi()
api.setAccessToken(token) api.setAccessToken(token)
spotify = api.service spotify = api.service
mainViewModel.spotify = api.service sharedViewModel.spotify = api.service
//Initiate Processes In Main Fragment //Initiate Processes In Main Fragment
val me = spotify?.me?.display_name val me = spotify?.me?.display_name
mainViewModel.userName.value = me sharedViewModel.userName.value = me
Log.i("Network",me!!) Log.i("Network",me!!)
} }
AuthenticationResponse.Type.ERROR -> { AuthenticationResponse.Type.ERROR -> {
Log.i("Network",response.error.toString()) Log.i("Network",response.error.toString())
@ -87,6 +92,25 @@ 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"
)
}
.build()
val spotifyExtra = restAdapter.create(SpotifyNewService::class.java)
sharedViewModel.spotifyExtra = spotifyExtra
}
private fun requestPermission() { private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
requestPermissions( requestPermissions(
@ -96,5 +120,13 @@ class MainActivity : AppCompatActivity() {
} }
} }
private fun authenticateSpotify() {
val builder = AuthenticationRequest.Builder(CLIENT_ID,AuthenticationResponse.Type.TOKEN,REDIRECT_URI)
.setShowDialog(true)
.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)
}
} }

View File

@ -1,15 +0,0 @@
package com.shabinder.musicForEveryone
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import kaaes.spotify.webapi.android.SpotifyService
class MainViewModel : ViewModel() {
var apiCame = MutableLiveData<Int>().apply { value = 0 }
var userName = MutableLiveData<String>().apply { value = "Placeholder" }
var spotify :SpotifyService? = null
}

View File

@ -0,0 +1,28 @@
package com.shabinder.musicForEveryone
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.kiulian.downloader.YoutubeDownloader
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
class SharedViewModel : ViewModel() {
var accessToken:String = ""
var userName = MutableLiveData<String>().apply { value = "Placeholder" }
var spotify :SpotifyService? = null
var spotifyExtra :SpotifyNewService? = null
var ytDownloader : YoutubeDownloader? = null
fun getTrackDetails(trackLink:String): Track?{
return spotify?.getTrack(trackLink)
}
fun getAlbumDetails(albumLink:String): Album?{
return spotify?.getAlbum(albumLink)
}
fun getPlaylistDetails(link:String): Playlist?{
return spotifyExtra?.getPlaylist(link)
}
}

View File

@ -0,0 +1,15 @@
package com.shabinder.musicForEveryone
import kaaes.spotify.webapi.android.models.Playlist
import retrofit.http.GET
import retrofit.http.Path
interface SpotifyNewService {
@GET("/playlists/{playlist_id}")
fun getPlaylist(@Path("playlist_id") playlistId: String?): Playlist?
}

View File

@ -1,21 +1,27 @@
package com.shabinder.musicForEveryone.fragments package com.shabinder.musicForEveryone.fragments
import android.os.Bundle import android.os.Bundle
import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.Toast
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.shabinder.musicForEveryone.MainViewModel import com.google.android.material.snackbar.Snackbar
import com.shabinder.musicForEveryone.R import com.shabinder.musicForEveryone.R
import com.shabinder.musicForEveryone.SharedViewModel
import com.shabinder.musicForEveryone.bindImage
import com.shabinder.musicForEveryone.databinding.MainFragmentBinding import com.shabinder.musicForEveryone.databinding.MainFragmentBinding
import com.shabinder.musicForEveryone.utils.YoutubeConnector
import kaaes.spotify.webapi.android.SpotifyService import kaaes.spotify.webapi.android.SpotifyService
class MainFragment : Fragment() { class MainFragment : Fragment() {
lateinit var binding:MainFragmentBinding lateinit var binding:MainFragmentBinding
private lateinit var mainViewModel: MainViewModel private lateinit var sharedViewModel: SharedViewModel
var spotify : SpotifyService? = null var spotify : SpotifyService? = null
override fun onCreateView( override fun onCreateView(
@ -23,17 +29,89 @@ class MainFragment : Fragment() {
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View { ): View {
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false) binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
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.btnGetDetails.setOnClickListener {
val spotifyLink = binding.spotifyLink.text.toString()
val link = spotifyLink.substringAfterLast('/' , "Error").substringBefore('?')
val type = spotifyLink.substringBeforeLast('/' , "Error").substringAfterLast('/')
Log.i("Fragment",link)
when(type){
"track" -> {
val trackObject = sharedViewModel.getTrackDetails(link)
Log.i("Fragment",trackObject?.name.toString())
binding.name.text = trackObject?.name ?: "Error"
var artistNames = ""
trackObject?.artists?.forEach { artistNames = artistNames.plus("${it.name} ,") }
binding.artist.text = artistNames
binding.popularity.text = trackObject?.popularity.toString()
binding.duration.text = ((trackObject?.duration_ms!! /1000/60).toString() + "minutes")
binding.albumName.text = trackObject.album.name
bindImage(binding.imageUrl, trackObject.album.images[0].url)
}
"album" -> {
val albumObject = sharedViewModel.getAlbumDetails(link)
Log.i("Fragment",albumObject?.name.toString())
binding.name.text = albumObject?.name ?: "Error"
var artistNames = ""
albumObject?.artists?.forEach { artistNames = artistNames.plus(", ${it.name}") }
binding.artist.text = artistNames
binding.popularity.text = albumObject?.popularity.toString()
binding.duration.visibility = View.GONE
binding.textView5.visibility = View.GONE
binding.albumName.text = albumObject?.name
bindImage(binding.imageUrl, albumObject?.images?.get(0)?.url)
}
"playlist" -> {
val playlistObject = sharedViewModel.getPlaylistDetails(link)
Log.i("Fragment",playlistObject?.name.toString())
binding.name.text = playlistObject?.name ?: "Error"
binding.artist.visibility = View.GONE
binding.textView1.visibility = View.GONE
binding.popularity.text = playlistObject?.followers?.total.toString()
binding.textview3.text = "Followers"
binding.duration.visibility = View.GONE
binding.textView5.visibility = View.GONE
binding.textView7.text = "Total Tracks"
binding.albumName.text = playlistObject?.tracks?.items?.size.toString()
bindImage(binding.imageUrl, playlistObject?.images?.get(0)?.url)
}
"episode" -> {showToast("Implementation Pending")}
"show" -> {showToast("Implementation Pending ")}
}
binding.spotifyLink.setText(link)
}
binding.btnDownload.setOnClickListener {
val data = YoutubeConnector.search(
"${binding.name.text} ${if(binding.artist.text != "TextView" ){binding.artist.text}else{""}}")
?.get(0)
if (data==null){showToast("Youtube Request Failed!")}else{
val ytDownloader = sharedViewModel.ytDownloader
val video = ytDownloader?.getVideo(data.id)
val details = video?.details()
Log.i("ytDownloader", details?.title()?:"Error")
binding.name.text = details?.title()
}
}
return binding.root return binding.root
} }
override fun onActivityCreated(savedInstanceState: Bundle?) { private fun showToast(message:String){
super.onActivityCreated(savedInstanceState) Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
mainViewModel = ViewModelProvider(this.requireActivity()).get(MainViewModel::class.java)
spotify = mainViewModel.spotify
mainViewModel.userName.observeForever {
binding.message.text = it
}
} }
} }

View File

@ -0,0 +1,66 @@
package com.shabinder.musicForEveryone.utils
import android.util.Log
import com.google.api.client.http.HttpRequestInitializer
import com.google.api.client.http.javanet.NetHttpTransport
import com.google.api.client.json.jackson2.JacksonFactory
import com.google.api.services.youtube.YouTube
import java.io.IOException
object YoutubeConnector {
private var youtube: YouTube? = null
private var query:YouTube.Search.List? = null
var apiKey:String = "AIzaSyDuRmMA_2mF56BjlhhNpa0SIbjMgjjFaEI"
var clientID : String = "1040727735015-er2mvvljt45cabkuqimsh3iabqvfpvms.apps.googleusercontent.com"
fun youtubeConnector() {
youtube =
YouTube.Builder(NetHttpTransport(), JacksonFactory(),
HttpRequestInitializer { })
.setApplicationName("Music For Everyone").build()
try {
query = youtube?.search()?.list("id,snippet")
query?.key = apiKey
query?.maxResults = 1
query?.type = "video"
query?.fields =
"items(id/kind,id/videoId,snippet/title,snippet/description,snippet/thumbnails/default/url)"
} catch (e: IOException) {
Log.i("YC", "Could not initialize: $e")
}
}
fun search(keywords: String?): List<VideoItem>? {
Log.i("YC searched for",keywords.toString())
if (youtube == null){youtubeConnector()}
query!!.q= keywords
return try {
val response = query!!.execute()
val results =
response.items
val items = mutableListOf<VideoItem>()
for (result in results) {
val item = VideoItem(
id = result.id.videoId,
title = result.snippet.title,
description = result.snippet.description,
thumbnailUrl = result.snippet.thumbnails.default.url
)
items.add(item)
Log.i("YC links received",item.id)
}
items
} catch (e: IOException) {
Log.d("YC", "Could not search: $e")
null
}
}
data class VideoItem(
val id:String,
val title:String,
val description: String,
val thumbnailUrl:String
)
}

View File

@ -3,6 +3,7 @@
xmlns:android="http://schemas.android.com/apk/res/android" xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"> xmlns:tools="http://schemas.android.com/tools">
<androidx.constraintlayout.widget.ConstraintLayout <androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/main" android:id="@+id/main"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -13,11 +14,156 @@
android:id="@+id/message" android:id="@+id/message"
android:layout_width="wrap_content" android:layout_width="wrap_content"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:text="MainFragment" android:text="MainFragment"
app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="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="24dp"
android:ems="10"
android:inputType="text"
android:text="Song Link From Spotify"
app:layout_constraintEnd_toStartOf="@+id/btn_getDetails"
app:layout_constraintStart_toStartOf="parent" app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" /> app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout> <Button
android:id="@+id/btn_getDetails"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Get Details"
app:layout_constraintBottom_toBottomOf="@+id/spotifyLink"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/spotifyLink"
app:layout_constraintTop_toTopOf="@+id/spotifyLink" />
<ImageView
android:id="@+id/imageUrl"
android:layout_width="180dp"
android:layout_height="200dp"
android:layout_marginTop="16dp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/spotifyLink"
tools:srcCompat="@tools:sample/avatars" />
<TextView
android:id="@+id/name"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:fontFamily="monospace"
android:text="TextView"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppCompat.Display1"
android:textStyle="bold"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/imageUrl" />
<TextView
android:id="@+id/textView1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Artist
"
app:layout_constraintBottom_toTopOf="@+id/textview3"
app:layout_constraintEnd_toStartOf="@+id/artist"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_download" />
<TextView
android:id="@+id/artist"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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="wrap_content"
android:layout_height="wrap_content"
android:text="Popularity"
app:layout_constraintBottom_toTopOf="@+id/textView5"
app:layout_constraintEnd_toStartOf="@+id/popularity"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView1" />
<TextView
android:id="@+id/popularity"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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="wrap_content"
android:layout_height="wrap_content"
android:text="Duration"
app:layout_constraintBottom_toTopOf="@+id/textView7"
app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textview3" />
<TextView
android:id="@+id/duration"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
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" />
<TextView
android:id="@+id/textView7"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="Album Name"
app:layout_constraintBottom_toTopOf="@+id/message"
app:layout_constraintEnd_toStartOf="@+id/albumName"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/textView5" />
<TextView
android:id="@+id/albumName"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="TextView"
app:layout_constraintBottom_toBottomOf="@+id/textView7"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0.5"
app:layout_constraintStart_toEndOf="@+id/textView7"
app:layout_constraintTop_toTopOf="@+id/textView7" />
<Button
android:id="@+id/btn_download"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="8dp"
android:text="Download"
app:layout_constraintEnd_toEndOf="@+id/name"
app:layout_constraintStart_toStartOf="@+id/name"
app:layout_constraintTop_toBottomOf="@+id/name" />
</androidx.constraintlayout.widget.ConstraintLayout>
</layout> </layout>

36
build.gradle Normal file
View File

@ -0,0 +1,36 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
ext{
kotlin_version = "1.3.72"
navigationVersion = '2.3.0'
}
repositories {
google()
jcenter()
}
dependencies {
classpath "com.android.tools.build:gradle:4.0.0"
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
//safe-Args
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:$navigationVersion"
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
google()
jcenter()
mavenCentral()
maven { url "https://jitpack.io" }
flatDir {
dirs 'libs'
}
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}

21
gradle.properties Normal file
View File

@ -0,0 +1,21 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
org.gradle.jvmargs=-Xmx2048m
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true
# AndroidX package structure to make it clearer which packages are bundled with the
# Android operating system, and which are packaged with your app"s APK
# https://developer.android.com/topic/libraries/support-library/androidx-rn
android.useAndroidX=true
# Automatically convert third-party libraries to use AndroidX
android.enableJetifier=true
# Kotlin code style for this project: "official" or "obsolete":
kotlin.code.style=official

2
settings.gradle Normal file
View File

@ -0,0 +1,2 @@
include ':app'
rootProject.name = "musicForEveryone"