mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-24 18:04:33 +01:00
Error Handling Improved,Shifted To client id:secret Flow(MoreLimit & Fast)
This commit is contained in:
parent
5d1974739e
commit
94f4560478
@ -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>
|
||||
|
BIN
app/src/main/ic_launcher-playstore.png
Normal file
BIN
app/src/main/ic_launcher-playstore.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 89 KiB |
@ -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")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
}
|
@ -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()
|
||||
}
|
||||
}
|
@ -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,11 +47,44 @@ 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){
|
||||
Log.i("Catch",e.toString())
|
||||
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")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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('/')
|
||||
val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
|
||||
type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
|
||||
|
||||
Log.i("Fragment", "$type : $link")
|
||||
|
||||
val adapter = TrackListAdapter()
|
||||
binding.trackList.adapter = adapter
|
||||
adapter.sharedViewModel = sharedViewModel
|
||||
when(type){
|
||||
"track" -> {
|
||||
sharedViewModel.uiScope.launch{
|
||||
val trackObject = sharedViewModel.getTrackDetails(link)
|
||||
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()
|
||||
|
||||
binding.imageView.visibility =View.VISIBLE
|
||||
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)
|
||||
val trackList = mutableListOf<Track>()
|
||||
trackList.add(trackObject!!)
|
||||
mainViewModel.trackList = trackList
|
||||
mainViewModel.coverUrl = trackObject.album!!.images?.get(0)!!.url!!
|
||||
bindImage(binding.imageView,mainViewModel.coverUrl)
|
||||
adapterConfig(trackList)
|
||||
|
||||
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) }
|
||||
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)
|
||||
val trackList = mutableListOf<Track>()
|
||||
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)
|
||||
val trackList = mutableListOf<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 ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
"album" -> {
|
||||
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) }
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
"playlist" -> {
|
||||
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) }
|
||||
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) }
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
"episode" -> {showToast("Implementation Pending")}
|
||||
"show" -> {showToast("Implementation Pending ")}
|
||||
}
|
||||
}
|
||||
binding.spotifyLink.setText(sharedViewModel.intentString)
|
||||
sharedViewModel.userName.observe(viewLifecycleOwner, Observer {
|
||||
//Waiting for Authentication to Finish with Spotify
|
||||
if (it != "Placeholder"){
|
||||
if(sharedViewModel.intentString != ""){binding.btnSearch.performClick()}
|
||||
}
|
||||
})
|
||||
|
||||
handleIntent()
|
||||
if(savedInstanceState != null && binding.linkSearch.text.toString() != ""){
|
||||
binding.linkSearch.setText(savedInstanceState["searchLink"].toString())
|
||||
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 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showToast(message:String){
|
||||
/**
|
||||
* Configure Recycler View Adapter
|
||||
**/
|
||||
private fun adapterConfig(trackList: List<Track>){
|
||||
adapter.trackList = trackList.toList()
|
||||
adapter.totalItems = trackList.size
|
||||
adapter.notifyDataSetChanged()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 != ""){
|
||||
if(sharedViewModel.intentString != ""){
|
||||
binding.btnSearch.performClick()
|
||||
setUiVisibility()
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
}
|
||||
}
|
@ -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 = ""
|
||||
|
||||
}
|
@ -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?:""}")
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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()
|
||||
.error(R.drawable.ic_musicplaceholder))
|
||||
.into(imgView)
|
||||
}
|
||||
|
||||
imgUrl?.let {
|
||||
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
|
||||
Glide.with(imgView.context)
|
||||
.load(imgUri)
|
||||
.apply(RequestOptions()
|
||||
.placeholder(R.drawable.ic_song_placeholder)
|
||||
.error(R.drawable.ic_musicplaceholder))
|
||||
.into(imgView)
|
||||
}
|
||||
}
|
@ -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)
|
@ -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?
|
||||
|
||||
}
|
@ -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 =
|
||||
|
Loading…
Reference in New Issue
Block a user