Error Handling Improved,Shifted To client id:secret Flow(MoreLimit & Fast)

This commit is contained in:
shabinder 2020-07-25 00:03:40 +05:30
parent 5d1974739e
commit 94f4560478
12 changed files with 521 additions and 251 deletions

View File

@ -26,9 +26,14 @@
<category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" />
</intent-filter>
</activity>
<activity android:name="com.shabinder.musicForEveryone.splash.SplashScreen"
android:label="@string/app_name"
android:theme="@style/Theme.Transparent">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>

Binary file not shown.

After

Width:  |  Height:  |  Size: 89 KiB

View File

@ -4,26 +4,22 @@ import android.Manifest
import android.app.DownloadManager
import android.content.Context
import android.content.Intent
import android.content.SharedPreferences
import android.net.ConnectivityManager
import android.os.Build
import android.os.Bundle
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.SpotifyService
import com.shabinder.musicForEveryone.utils.SpotifyServiceToken
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 kotlinx.coroutines.launch
import okhttp3.Interceptor
import okhttp3.OkHttpClient
@ -32,30 +28,43 @@ import okhttp3.Response
import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory
@Suppress("DEPRECATION")
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"
var token :String =""
var spotify: SpotifyService? = null
lateinit var sharedViewModel: SharedViewModel
private var ytDownloader : YoutubeDownloader? = null
private var spotifyService : SpotifyService? = null
private var spotifyServiceToken : SpotifyServiceToken? = null
private var downloadManager : DownloadManager? = null
// private val redirectUri = "musicforeveryone://callback"
private val clientId:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
private val clientSecret:String = "02ca2d4021a7452dae2328b47a6e8fe8"
private var isConnected: Boolean = false
private var sharedPref :SharedPreferences? = null
private var token :String =""
private 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)
//TODO Use Coroutines
if(spotify==null){
sharedPref = this.getPreferences(Context.MODE_PRIVATE)
// if(sharedPref?.contains("token")!! && (sharedPref?.getLong("time",System.currentTimeMillis()/1000/60/60)!! < (System.currentTimeMillis()/1000/60/60)) ){
// val savedToken = sharedPref?.getString("token","error")!!
// sharedViewModel.accessToken.value = savedToken
// Log.i("SharedPrefs Token:",savedToken)
// token = savedToken
//
// implementSpotifyService(savedToken)
// }else{authenticateSpotify()}
if(sharedViewModel.spotifyService == null){
authenticateSpotify()
}else{
implementSpotifyService(sharedViewModel.accessToken.value!!)
}
requestPermission()
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"}
@ -68,69 +77,22 @@ class MainActivity : AppCompatActivity() ,DownloadHelper{
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
}
}
}
}
isConnected = isOnline()
sharedViewModel.isConnected.value = isConnected
Log.i("Connection Status",isConnected.toString())
override fun onActivityResult(
requestCode: Int,
resultCode: Int,
intent: Intent?
) {
super.onActivityResult(requestCode, resultCode, intent)
// Check if result comes from the correct activity
if (requestCode == LoginActivity.REQUEST_CODE) {
val response = AuthenticationClient.getResponse(resultCode, intent)
when (response.type) {
AuthenticationResponse.Type.TOKEN -> {
Log.i("Network",response.accessToken.toString())
token = response.accessToken
sharedViewModel.accessToken = response.accessToken
//Implementing My Own Spotify Requests
implementSpotifyExtra()
val api = SpotifyApi()
api.setAccessToken(token)
spotify = api.service
sharedViewModel.spotify = api.service
//Initiate Processes In Main Fragment
sharedViewModel.uiScope.launch {
val me = spotifyExtra?.getMe()?.display_name
sharedViewModel.userName.value = "Logged in as: $me"
Log.i("Network","Hello, " + me!!)
}
sharedViewModel.userName.observe(this, Observer {
binding.message.text = it
})
}
AuthenticationResponse.Type.ERROR -> {
Log.i("Network",response.error.toString())
}
else -> {
}
}
}
handleIntentFromExternalActivity()
}
/**
* Adding my own new Spotify Web Api Requests!
* */
private fun implementSpotifyExtra() {
private fun implementSpotifyService(token:String) {
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request =
chain.request().newBuilder().addHeader(
@ -146,17 +108,89 @@ class MainActivity : AppCompatActivity() ,DownloadHelper{
.add(KotlinJsonAdapterFactory())
.build()
val retrofit: Retrofit =
Retrofit.Builder()
val 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
spotifyService = retrofit.create(SpotifyService::class.java)
sharedViewModel.spotifyService = spotifyService
}
private fun getSpotifyToken(){
val httpClient2: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient2.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response {
val request: Request =
chain.request().newBuilder().addHeader(
"Authorization",
"Basic ${android.util.Base64.encodeToString("$clientId:$clientSecret".toByteArray(),android.util.Base64.NO_WRAP)}"
).build()
return chain.proceed(request)
}
})
val moshi = Moshi.Builder()
.add(KotlinJsonAdapterFactory())
.build()
val retrofit2 = Retrofit.Builder()
.baseUrl("https://accounts.spotify.com/")
.client(httpClient2.build())
.addConverterFactory(MoshiConverterFactory.create(moshi))
.build()
spotifyServiceToken = retrofit2.create(SpotifyServiceToken::class.java)
}
private fun authenticateSpotify() {
if (spotifyServiceToken == null) {
getSpotifyToken()
}
sharedViewModel.uiScope.launch {
if (isConnected) {
Log.i("Post Request", "Made")
token = spotifyServiceToken!!.getToken()!!.access_token
implementSpotifyService(token)
Log.i("Post Request", token)
sharedViewModel.accessToken.value = token
saveToken(token)
}else{
Log.i("network", "unavailable")
// sharedViewModel.showAlertDialog(resources,this@MainActivity)
}
}
}
private fun saveToken(token:String) {
with (sharedPref?.edit()) {
this?.let {
putString("token", token)
putLong("time",(System.currentTimeMillis()/1000/60/60))
commit()
}
}
}
private fun handleIntentFromExternalActivity() {
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
}
}
}
}
private fun isOnline(): Boolean {
val cm =
getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = cm.activeNetworkInfo
return netInfo != null && netInfo.isConnectedOrConnecting
}
private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
@ -167,13 +201,66 @@ class MainActivity : AppCompatActivity() ,DownloadHelper{
}
}
override fun onSaveInstanceState(savedInstanceState:Bundle) {
savedInstanceState.putString("token",token)
super.onSaveInstanceState(savedInstanceState)
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
if (savedInstanceState.getString("token") ==""){
super.onRestoreInstanceState(savedInstanceState)
}else{
implementSpotifyService(savedInstanceState.getString("token")!!)
super.onRestoreInstanceState(savedInstanceState)
}
}
/*
private fun authenticateSpotify() {
val builder = AuthenticationRequest.Builder(CLIENT_ID,AuthenticationResponse.Type.TOKEN,REDIRECT_URI)
.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 builder = AuthenticationRequest.Builder(clientId,AuthenticationResponse.Type.TOKEN,redirectUri)
.setScopes(arrayOf("user-read-private"))
// .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(
requestCode: Int,
resultCode: Int,
intent: Intent?
) {
super.onActivityResult(requestCode, resultCode, intent)
// Check if result comes from the correct activity
if (requestCode == LoginActivity.REQUEST_CODE) {
val response = AuthenticationClient.getResponse(resultCode, intent)
when (response.type) {
AuthenticationResponse.Type.TOKEN -> {
Log.i("Network",response.accessToken.toString())
token = response.accessToken
sharedViewModel.accessToken = response.accessToken
//Implementing My Own Spotify Requests
implementSpotifyService(token)
sharedViewModel.uiScope.launch {
val me = spotifyService?.getMe()?.display_name
sharedViewModel.userName.value = "Logged in as: $me"
Log.i("Network","Hello, " + me!!)
}
sharedViewModel.userName.observe(this, Observer {
binding.message.text = it
})
}
AuthenticationResponse.Type.ERROR -> {
Log.i("Network",response.error.toString())
}
else -> {
Log.i("Network","Something Weird Happened While Authenticating")
}
}
}
}
*/
}

View File

@ -1,39 +1,40 @@
package com.shabinder.musicForEveryone
import android.app.DownloadManager
import android.content.Context
import android.content.res.Resources
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.musicForEveryone.utils.SpotifyNewService
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 com.google.android.material.dialog.MaterialAlertDialogBuilder
import com.shabinder.musicForEveryone.models.Album
import com.shabinder.musicForEveryone.models.Playlist
import com.shabinder.musicForEveryone.models.Track
import com.shabinder.musicForEveryone.utils.SpotifyService
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
var spotifyExtra : SpotifyNewService? = null
var accessToken = MutableLiveData<String>().apply { value = "" }
var spotifyService : SpotifyService? = null
var ytDownloader : YoutubeDownloader? = null
var downloadManager : DownloadManager? = null
var isConnected = MutableLiveData<Boolean>().apply { value = false }
var viewModelJob = Job()
private var viewModelJob = Job()
val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
suspend fun getTrackDetails(trackLink:String): Track?{
return spotifyExtra?.getTrack(trackLink)
return spotifyService?.getTrack(trackLink)
}
suspend fun getAlbumDetails(albumLink:String): Album?{
return spotifyExtra?.getAlbum(albumLink)
return spotifyService?.getAlbum(albumLink)
}
suspend fun getPlaylistDetails(link:String): Playlist?{
return spotifyExtra?.getPlaylist(link)
return spotifyService?.getPlaylist(link)
}
@ -41,4 +42,15 @@ class SharedViewModel : ViewModel() {
super.onCleared()
viewModelJob.cancel()
}
fun showAlertDialog(resources:Resources,context: Context){
val dialog = MaterialAlertDialogBuilder(context,R.style.AlertDialogTheme)
.setTitle(resources.getString(R.string.title))
.setMessage(resources.getString(R.string.supporting_text))
.setPositiveButton(resources.getString(R.string.cancel)) { _, _ ->
// Respond to neutral button press
}
.setBackground(resources.getDrawable(R.drawable.gradient))
.show()
}
}

View File

@ -8,18 +8,29 @@ import android.util.Log
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.fragments.MainFragment
import com.shabinder.musicForEveryone.models.Track
import com.shabinder.musicForEveryone.utils.YoutubeInterface
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.io.File
interface DownloadHelper {
/**
* Function To Download All Tracks Available in a List
**/
suspend fun downloadAllTracks(trackList : List<Track>, ytDownloader: YoutubeDownloader?, downloadManager: DownloadManager?) {
trackList.forEach { downloadTrack(null,ytDownloader,downloadManager,"${it.name} ${it.artists?.get(0)?.name ?:""}") }
}
suspend fun downloadTrack(
mainFragment: MainFragment?,
ytDownloader: YoutubeDownloader?,
downloadManager: DownloadManager?,
searchQuery: String
) {
withContext(Dispatchers.IO) {
val data = YoutubeInterface.search(searchQuery)?.get(0)
if (data == null) {
@ -36,12 +47,45 @@ interface DownloadHelper {
Log.i("DHelper Link Found", audioUrl)
if (audioUrl != null) {
downloadFile(audioUrl, downloadManager, details!!.title())
withContext(Dispatchers.Main){
mainFragment?.showToast("Download Started")
}
} else {
Log.i("YT audio url is null", format.toString())
}
}catch (e:ArrayIndexOutOfBoundsException){
try{
val format: Format =
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
val audioUrl = format.url()
Log.i("DHelper Link Found", audioUrl)
if (audioUrl != null) {
downloadFile(audioUrl, downloadManager, details!!.title())
withContext(Dispatchers.Main){
mainFragment?.showToast("Download Started")
}
} else {
Log.i("YT audio url is null", format.toString())
}
}catch (e:ArrayIndexOutOfBoundsException){
try{
val format: Format =
video?.findAudioWithQuality(AudioQuality.high)?.get(0) as Format
val audioUrl = format.url()
Log.i("DHelper Link Found", audioUrl)
if (audioUrl != null) {
downloadFile(audioUrl, downloadManager, details!!.title())
withContext(Dispatchers.Main){
mainFragment?.showToast("Download Started")
}
} else {
Log.i("YT audio url is null", format.toString())
}
}catch(e:ArrayIndexOutOfBoundsException){
Log.i("Catch",e.toString())
}
}
}
}
@ -70,6 +114,7 @@ interface DownloadHelper {
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
downloadManager?.enqueue(request)
Log.i("DownloadManager", "Download Request Sent")
}
}

View File

@ -1,6 +1,12 @@
package com.shabinder.musicForEveryone.fragments
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.net.ConnectivityManager
import android.net.Uri
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.util.Log
import android.view.LayoutInflater
import android.view.View
@ -14,19 +20,25 @@ import com.shabinder.musicForEveryone.R
import com.shabinder.musicForEveryone.SharedViewModel
import com.shabinder.musicForEveryone.databinding.MainFragmentBinding
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper
import com.shabinder.musicForEveryone.models.Track
import com.shabinder.musicForEveryone.recyclerView.TrackListAdapter
import com.shabinder.musicForEveryone.utils.SpotifyService
import com.shabinder.musicForEveryone.utils.bindImage
import kaaes.spotify.webapi.android.SpotifyService
import kaaes.spotify.webapi.android.models.Track
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Suppress("DEPRECATION")
class MainFragment : Fragment(),DownloadHelper {
lateinit var binding:MainFragmentBinding
private lateinit var binding:MainFragmentBinding
private lateinit var mainViewModel: MainViewModel
private lateinit var sharedViewModel: SharedViewModel
var spotify : SpotifyService? = null
var type:String = ""
var spotifyLink = ""
private lateinit var adapter:TrackListAdapter
private var spotifyService : SpotifyService? = null
private var type:String = ""
private var spotifyLink = ""
private var i: Intent? = null
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle?
@ -34,111 +46,211 @@ class MainFragment : Fragment(),DownloadHelper {
binding = DataBindingUtil.inflate(inflater,R.layout.main_fragment,container,false)
sharedViewModel = ViewModelProvider(this.requireActivity()).get(SharedViewModel::class.java)
spotify = sharedViewModel.spotify
mainViewModel = ViewModelProvider(this).get(MainViewModel::class.java)
spotifyService = sharedViewModel.spotifyService
val spanStringBuilder = SpannableStringBuilder()
spanStringBuilder.append(getText(R.string.d_one)).append("\n")
spanStringBuilder.append(getText(R.string.d_two)).append("\n")
spanStringBuilder.append(getText(R.string.d_three)).append("\n")
binding.usage.text = spanStringBuilder
openSpotifyButton()
binding.btnSearch.setOnClickListener {
spotifyLink = binding.spotifyLink.text.toString()
sharedViewModel.isConnected.value = isOnline()
spotifyLink = binding.linkSearch.text.toString()
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
Log.i("Fragment", "$type : $link")
val adapter = TrackListAdapter()
if (type == "Error" || link == "Error") {
showToast("Please Check Your Link!")
} else if(sharedViewModel.isConnected.value == false){
sharedViewModel.showAlertDialog(resources,requireContext())
}
else {
adapter = TrackListAdapter()
binding.trackList.adapter = adapter
adapter.sharedViewModel = sharedViewModel
adapter.mainFragment = this
setUiVisibility()
if(mainViewModel.searchLink == spotifyLink){
//it's a Device Configuration Change
adapterConfig(mainViewModel.trackList)
bindImage(binding.imageView,mainViewModel.coverUrl)
}else{
when (type) {
"track" -> {
mainViewModel.searchLink = spotifyLink
sharedViewModel.uiScope.launch {
val trackObject = sharedViewModel.getTrackDetails(link)
binding.imageView.visibility =View.VISIBLE
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())
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) }
mainViewModel.trackList = trackList
mainViewModel.coverUrl = trackObject.album!!.images?.get(0)!!.url!!
bindImage(binding.imageView,mainViewModel.coverUrl)
adapterConfig(trackList)
binding.btnDownloadAll.setOnClickListener {
sharedViewModel.uiScope.launch {
withContext(Dispatchers.IO) {
downloadAllTracks(
trackList,
sharedViewModel.ytDownloader,
sharedViewModel.downloadManager
)
}
}
}
}
}
"album" -> {
mainViewModel.searchLink = spotifyLink
sharedViewModel.uiScope.launch {
val albumObject = sharedViewModel.getAlbumDetails(link)
// binding.titleView.text = albumObject!!.name
// binding.titleView.visibility =View.VISIBLE
binding.imageView.visibility =View.VISIBLE
binding.btnDownloadAll.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)
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) }
albumObject!!.tracks?.items?.forEach { trackList.add(it!!) }
mainViewModel.trackList = trackList
mainViewModel.coverUrl = albumObject.images?.get(0)!!.url!!
bindImage(binding.imageView,mainViewModel.coverUrl)
adapter.isAlbum = true
adapterConfig(trackList)
binding.btnDownloadAll.setOnClickListener {
sharedViewModel.uiScope.launch {
withContext(Dispatchers.IO) {
downloadAllTracks(
trackList,
sharedViewModel.ytDownloader,
sharedViewModel.downloadManager
)
}
}
}
}
}
"playlist" -> {
mainViewModel.searchLink = spotifyLink
sharedViewModel.uiScope.launch {
val playlistObject = sharedViewModel.getPlaylistDetails(link)
binding.btnDownloadAll.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.playlistOwner.visibility =View.VISIBLE
// binding.playlistOwner.text = "by: ${playlistObject.owner.display_name}"
val trackList = mutableListOf<Track>()
playlistObject!!.tracks?.items!!.forEach { trackList.add(it.track) }
playlistObject!!.tracks?.items!!.forEach { trackList.add(it?.track!!) }
mainViewModel.trackList = trackList
mainViewModel.coverUrl = playlistObject.images?.get(0)!!.url!!
bindImage(binding.imageView,mainViewModel.coverUrl)
adapterConfig(trackList)
binding.btnDownloadAll.setOnClickListener {
sharedViewModel.uiScope.launch {
withContext(Dispatchers.IO) {
downloadAllTracks(
trackList,
sharedViewModel.ytDownloader,
sharedViewModel.downloadManager
)
}
}
}
}
}
"episode" -> {
showToast("Implementation Pending")
}
"show" -> {
showToast("Implementation Pending ")
}
}
}
}
}
handleIntent()
if(savedInstanceState != null && binding.linkSearch.text.toString() != ""){
binding.linkSearch.setText(savedInstanceState["searchLink"].toString())
binding.btnSearch.performClick()
setUiVisibility()
}
return binding.root
}
private fun openSpotifyButton() {
val manager: PackageManager = requireActivity().packageManager
try {
i = manager.getLaunchIntentForPackage("com.spotify.music")
if (i == null) throw PackageManager.NameNotFoundException()
i?.addCategory(Intent.CATEGORY_LAUNCHER)
binding.btnOpenSpotify.setOnClickListener { startActivity(i) }
} catch (e: PackageManager.NameNotFoundException) {
binding.textView.text = getString(R.string.spotify_not_installed)
binding.btnOpenSpotify.text = getString(R.string.spotify_web_link)
val uri: Uri =
Uri.parse("http://open.spotify.com")
val intent = Intent(Intent.ACTION_VIEW, uri)
binding.btnOpenSpotify.setOnClickListener {
startActivity(intent)
}
}
}
/**
* Configure Recycler View Adapter
**/
private fun adapterConfig(trackList: List<Track>){
adapter.trackList = trackList.toList()
adapter.totalItems = trackList.size
adapter.notifyDataSetChanged()
Log.i("Adapter",trackList.size.toString())
bindImage(binding.imageView, playlistObject.images[0].url)
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) }
}
/**
* Make Ui elements Visible
**/
private fun setUiVisibility() {
binding.btnDownloadAll.visibility =View.VISIBLE
binding.titleView.visibility = View.GONE
binding.openSpotify.visibility = View.GONE
binding.trackList.visibility = View.VISIBLE
}
"episode" -> {showToast("Implementation Pending")}
"show" -> {showToast("Implementation Pending ")}
}
}
binding.spotifyLink.setText(sharedViewModel.intentString)
sharedViewModel.userName.observe(viewLifecycleOwner, Observer {
/**
* Handle Intent If there is any!
**/
private fun handleIntent() {
binding.linkSearch.setText(sharedViewModel.intentString)
sharedViewModel.accessToken.observe(viewLifecycleOwner, Observer {
//Waiting for Authentication to Finish with Spotify
if (it != "Placeholder"){
if(sharedViewModel.intentString != ""){binding.btnSearch.performClick()}
if (it != ""){
if(sharedViewModel.intentString != ""){
binding.btnSearch.performClick()
setUiVisibility()
}
}
})
return binding.root
}
private fun downloadAllTracks(trackList : List<Track>) {
sharedViewModel.uiScope.launch {
trackList.forEach { downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${it.name} ${it.artists[0].name?:""}") }
}
}
private fun showToast(message:String){
/**
* Util. Function to create toasts!
**/
fun showToast(message:String){
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
}
private fun isOnline(): Boolean {
val cm =
requireActivity().getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
val netInfo = cm.activeNetworkInfo
return netInfo != null && netInfo.isConnectedOrConnecting
}
override fun onSaveInstanceState(outState: Bundle) {
super.onSaveInstanceState(outState)
outState.putCharSequence("searchLink",mainViewModel.searchLink)
}
}

View File

@ -0,0 +1,11 @@
package com.shabinder.musicForEveryone.fragments
import androidx.lifecycle.ViewModel
import com.shabinder.musicForEveryone.models.Track
class MainViewModel: ViewModel() {
var searchLink:String = ""
var trackList = mutableListOf<Track>()
var coverUrl:String = ""
}

View File

@ -10,8 +10,9 @@ import androidx.recyclerview.widget.RecyclerView
import com.shabinder.musicForEveryone.R
import com.shabinder.musicForEveryone.SharedViewModel
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper
import com.shabinder.musicForEveryone.fragments.MainFragment
import com.shabinder.musicForEveryone.models.Track
import com.shabinder.musicForEveryone.utils.bindImage
import kaaes.spotify.webapi.android.models.Track
import kotlinx.coroutines.launch
class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),DownloadHelper {
@ -19,6 +20,8 @@ class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),Downl
var trackList = listOf<Track>()
var totalItems:Int = 0
var sharedViewModel = SharedViewModel()
var isAlbum:Boolean = false
var mainFragment:MainFragment? = null
override fun getItemCount():Int = totalItems
@ -31,16 +34,16 @@ 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)
if(totalItems == 1 || isAlbum){holder.coverImage.visibility = View.GONE}else{
bindImage(holder.coverImage, item.album!!.images?.get(0)?.url)
}
holder.trackName.text = "${if(item.name.length > 17){"${item.name.subSequence(0,16)}..."}else{item.name}}"
holder.artistName.text = "${item.artists[0]?.name?:""}..."
holder.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}"
holder.artistName.text = "${item.artists?.get(0)?.name?:""}..."
holder.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
holder.downloadBtn.setOnClickListener{
sharedViewModel.uiScope.launch {
downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${item.name} ${item.artists[0].name?:""}")
downloadTrack(mainFragment,sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${item.name} ${item.artists?.get(0)!!.name?:""}")
}
}

View File

@ -7,17 +7,15 @@ import com.bumptech.glide.Glide
import com.bumptech.glide.request.RequestOptions
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()
.placeholder(R.drawable.ic_song_placeholder)
.error(R.drawable.ic_musicplaceholder))
.into(imgView)
}
}

View File

@ -1,36 +0,0 @@
package com.shabinder.musicForEveryone.utils
import kaaes.spotify.webapi.android.models.*
import retrofit2.http.GET
import retrofit2.http.Path
interface SpotifyNewService {
@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)

View File

@ -0,0 +1,35 @@
package com.shabinder.musicForEveryone.utils
import com.shabinder.musicForEveryone.models.*
import retrofit2.http.*
interface SpotifyService {
@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("episodes/{id}")
suspend fun getEpisode(@Path("id") var1: String?): Track?
@GET("shows/{id}")
suspend fun getShow(@Path("id") var1: String?): Track?
@GET("albums/{id}")
suspend fun getAlbum(@Path("id") var1: String?): Album?
@GET("me")
suspend fun getMe(): UserPrivate?
}
interface SpotifyServiceToken{
@POST("api/token")
@FormUrlEncoded
suspend fun getToken(@Field("grant_type") grant_type:String = "client_credentials"):Token?
}

View File

@ -10,10 +10,8 @@ import java.io.IOException
object YoutubeInterface {
private var youtube: YouTube? = null
private var query:YouTube.Search.List? = null
var apiKey:String = "AIzaSyDuRmMA_2mF56BjlhhNpa0SIbjMgjjFaEI"
var apiKey2:String = "AIzaSyCotyqgqmz5qw4-IH0tiezIrIIDHLI2yNs"
var clientID : String = "1040727735015-er2mvvljt45cabkuqimsh3iabqvfpvms.apps.googleusercontent.com"
private var apiKey:String = "AIzaSyDuRmMA_2mF56BjlhhNpa0SIbjMgjjFaEI"
private var apiKey2:String = "AIzaSyCotyqgqmz5qw4-IH0tiezIrIIDHLI2yNs"
fun youtubeConnector() {
youtube =