mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-25 02:14:32 +01:00
Repo refreshed and cleaned,Basic YT Functionality Added(TODO Coroutines!)
This commit is contained in:
parent
3cb3162beb
commit
8fcb22ab5d
@ -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">
|
||||||
|
@ -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'
|
||||||
|
@ -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" />
|
||||||
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
|
||||||
|
|
||||||
}
|
|
@ -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)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -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?
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
@ -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
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -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
|
||||||
|
)
|
||||||
|
|
||||||
|
}
|
@ -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
36
build.gradle
Normal 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
21
gradle.properties
Normal 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
2
settings.gradle
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
include ':app'
|
||||||
|
rootProject.name = "musicForEveryone"
|
Loading…
Reference in New Issue
Block a user