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" /> <category android:name="android.intent.category.DEFAULT" />
<data android:mimeType="text/plain" /> <data android:mimeType="text/plain" />
</intent-filter> </intent-filter>
</activity>
<activity android:name="com.shabinder.musicForEveryone.splash.SplashScreen"
android:label="@string/app_name"
android:theme="@style/Theme.Transparent">
<intent-filter> <intent-filter>
<action android:name="android.intent.action.MAIN" /> <action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" /> <category android:name="android.intent.category.LAUNCHER" />
</intent-filter> </intent-filter>
</activity> </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.app.DownloadManager
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences
import android.net.ConnectivityManager
import android.os.Build import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.util.Log 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.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.musicForEveryone.databinding.MainActivityBinding import com.shabinder.musicForEveryone.databinding.MainActivityBinding
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper 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.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.Moshi
import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory import com.squareup.moshi.kotlin.reflect.KotlinJsonAdapterFactory
import kaaes.spotify.webapi.android.SpotifyApi
import kaaes.spotify.webapi.android.SpotifyService
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.Interceptor import okhttp3.Interceptor
import okhttp3.OkHttpClient import okhttp3.OkHttpClient
@ -32,30 +28,43 @@ import okhttp3.Response
import retrofit2.Retrofit import retrofit2.Retrofit
import retrofit2.converter.moshi.MoshiConverterFactory import retrofit2.converter.moshi.MoshiConverterFactory
@Suppress("DEPRECATION")
class MainActivity : AppCompatActivity() ,DownloadHelper{ class MainActivity : AppCompatActivity() ,DownloadHelper{
private lateinit var binding: MainActivityBinding private lateinit var binding: MainActivityBinding
var ytDownloader : YoutubeDownloader? = null private var ytDownloader : YoutubeDownloader? = null
var spotifyExtra : SpotifyNewService? = null private var spotifyService : SpotifyService? = null
var downloadManager : DownloadManager? = null private var spotifyServiceToken : SpotifyServiceToken? = null
val REDIRECT_URI = "musicforeveryone://callback" private var downloadManager : DownloadManager? = null
val CLIENT_ID:String = "694d8bf4f6ec420fa66ea7fb4c68f89d" // private val redirectUri = "musicforeveryone://callback"
var token :String ="" private val clientId:String = "694d8bf4f6ec420fa66ea7fb4c68f89d"
var spotify: SpotifyService? = null private val clientSecret:String = "02ca2d4021a7452dae2328b47a6e8fe8"
lateinit var sharedViewModel: SharedViewModel private var isConnected: Boolean = false
private var sharedPref :SharedPreferences? = null
private var token :String =""
private 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)
sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java) sharedViewModel = ViewModelProvider(this).get(SharedViewModel::class.java)
// val policy = sharedPref = this.getPreferences(Context.MODE_PRIVATE)
// StrictMode.ThreadPolicy.Builder().permitAll().build()
// StrictMode.setThreadPolicy(policy) // if(sharedPref?.contains("token")!! && (sharedPref?.getLong("time",System.currentTimeMillis()/1000/60/60)!! < (System.currentTimeMillis()/1000/60/60)) ){
//TODO Use Coroutines // val savedToken = sharedPref?.getString("token","error")!!
if(spotify==null){ // sharedViewModel.accessToken.value = savedToken
// Log.i("SharedPrefs Token:",savedToken)
// token = savedToken
//
// implementSpotifyService(savedToken)
// }else{authenticateSpotify()}
if(sharedViewModel.spotifyService == null){
authenticateSpotify() authenticateSpotify()
}else{
implementSpotifyService(sharedViewModel.accessToken.value!!)
} }
requestPermission() requestPermission()
//Object to download From Youtube {"https://github.com/sealedtx/java-youtube-downloader"} //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 downloadManager = getSystemService(Context.DOWNLOAD_SERVICE) as DownloadManager
sharedViewModel.downloadManager = downloadManager sharedViewModel.downloadManager = downloadManager
if (intent?.action == Intent.ACTION_SEND) { isConnected = isOnline()
if ("text/plain" == intent.type) { sharedViewModel.isConnected.value = isConnected
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
Log.i("Intent Received",it) Log.i("Connection Status",isConnected.toString())
sharedViewModel.intentString = it
}
}
}
}
override fun onActivityResult( handleIntentFromExternalActivity()
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 -> {
}
}
}
} }
/** /**
* Adding my own new Spotify Web Api Requests! * Adding my own new Spotify Web Api Requests!
* */ * */
private fun implementSpotifyExtra() { private fun implementSpotifyService(token:String) {
val httpClient: OkHttpClient.Builder = OkHttpClient.Builder() val httpClient: OkHttpClient.Builder = OkHttpClient.Builder()
httpClient.addInterceptor(object : Interceptor { httpClient.addInterceptor(object : Interceptor {
override fun intercept(chain: Interceptor.Chain): Response { override fun intercept(chain: Interceptor.Chain): Response {
val request: Request = val request: Request =
chain.request().newBuilder().addHeader( chain.request().newBuilder().addHeader(
@ -146,17 +108,89 @@ class MainActivity : AppCompatActivity() ,DownloadHelper{
.add(KotlinJsonAdapterFactory()) .add(KotlinJsonAdapterFactory())
.build() .build()
val retrofit: Retrofit = val retrofit = Retrofit.Builder()
Retrofit.Builder()
.baseUrl("https://api.spotify.com/v1/") .baseUrl("https://api.spotify.com/v1/")
.client(httpClient.build()) .client(httpClient.build())
.addConverterFactory(MoshiConverterFactory.create(moshi)) .addConverterFactory(MoshiConverterFactory.create(moshi))
.build() .build()
spotifyExtra = retrofit.create(SpotifyNewService::class.java) spotifyService = retrofit.create(SpotifyService::class.java)
sharedViewModel.spotifyExtra = spotifyExtra 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() { private fun requestPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { 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() { private fun authenticateSpotify() {
val builder = AuthenticationRequest.Builder(CLIENT_ID,AuthenticationResponse.Type.TOKEN,REDIRECT_URI) val builder = AuthenticationRequest.Builder(clientId,AuthenticationResponse.Type.TOKEN,redirectUri)
.setShowDialog(false) .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")) // .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() val request: AuthenticationRequest = builder.build()
AuthenticationClient.openLoginActivity(this, LoginActivity.REQUEST_CODE, request) 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 package com.shabinder.musicForEveryone
import android.app.DownloadManager import android.app.DownloadManager
import android.content.Context
import android.content.res.Resources
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import com.github.kiulian.downloader.YoutubeDownloader import com.github.kiulian.downloader.YoutubeDownloader
import com.shabinder.musicForEveryone.utils.SpotifyNewService import com.google.android.material.dialog.MaterialAlertDialogBuilder
import kaaes.spotify.webapi.android.SpotifyService import com.shabinder.musicForEveryone.models.Album
import kaaes.spotify.webapi.android.models.Album import com.shabinder.musicForEveryone.models.Playlist
import kaaes.spotify.webapi.android.models.Playlist import com.shabinder.musicForEveryone.models.Track
import kaaes.spotify.webapi.android.models.Track import com.shabinder.musicForEveryone.utils.SpotifyService
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
class SharedViewModel : ViewModel() { class SharedViewModel : ViewModel() {
var intentString = "" var intentString = ""
var accessToken:String = "" var accessToken = MutableLiveData<String>().apply { value = "" }
var userName = MutableLiveData<String>().apply { value = "Placeholder" } var spotifyService : SpotifyService? = null
var spotify :SpotifyService? = null
var spotifyExtra : SpotifyNewService? = null
var ytDownloader : YoutubeDownloader? = null var ytDownloader : YoutubeDownloader? = null
var downloadManager : DownloadManager? = 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) val uiScope = CoroutineScope(Dispatchers.Main + viewModelJob)
suspend fun getTrackDetails(trackLink:String): Track?{ suspend fun getTrackDetails(trackLink:String): Track?{
return spotifyExtra?.getTrack(trackLink) return spotifyService?.getTrack(trackLink)
} }
suspend fun getAlbumDetails(albumLink:String): Album?{ suspend fun getAlbumDetails(albumLink:String): Album?{
return spotifyExtra?.getAlbum(albumLink) return spotifyService?.getAlbum(albumLink)
} }
suspend fun getPlaylistDetails(link:String): Playlist?{ suspend fun getPlaylistDetails(link:String): Playlist?{
return spotifyExtra?.getPlaylist(link) return spotifyService?.getPlaylist(link)
} }
@ -41,4 +42,15 @@ class SharedViewModel : ViewModel() {
super.onCleared() super.onCleared()
viewModelJob.cancel() 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.YoutubeDownloader
import com.github.kiulian.downloader.model.formats.Format import com.github.kiulian.downloader.model.formats.Format
import com.github.kiulian.downloader.model.quality.AudioQuality 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 com.shabinder.musicForEveryone.utils.YoutubeInterface
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import java.io.File import java.io.File
interface DownloadHelper { 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( suspend fun downloadTrack(
mainFragment: MainFragment?,
ytDownloader: YoutubeDownloader?, ytDownloader: YoutubeDownloader?,
downloadManager: DownloadManager?, downloadManager: DownloadManager?,
searchQuery: String searchQuery: String
) { ) {
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val data = YoutubeInterface.search(searchQuery)?.get(0) val data = YoutubeInterface.search(searchQuery)?.get(0)
if (data == null) { if (data == null) {
@ -36,11 +47,44 @@ interface DownloadHelper {
Log.i("DHelper Link Found", audioUrl) Log.i("DHelper Link Found", audioUrl)
if (audioUrl != null) { if (audioUrl != null) {
downloadFile(audioUrl, downloadManager, details!!.title()) downloadFile(audioUrl, downloadManager, details!!.title())
withContext(Dispatchers.Main){
mainFragment?.showToast("Download Started")
}
} else { } else {
Log.i("YT audio url is null", format.toString()) Log.i("YT audio url is null", format.toString())
} }
}catch (e:ArrayIndexOutOfBoundsException){ }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) .setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
downloadManager?.enqueue(request) downloadManager?.enqueue(request)
Log.i("DownloadManager", "Download Request Sent") Log.i("DownloadManager", "Download Request Sent")
} }
} }

View File

@ -1,6 +1,12 @@
package com.shabinder.musicForEveryone.fragments 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.os.Bundle
import android.text.SpannableStringBuilder
import android.util.Log import android.util.Log
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.View import android.view.View
@ -14,19 +20,25 @@ import com.shabinder.musicForEveryone.R
import com.shabinder.musicForEveryone.SharedViewModel import com.shabinder.musicForEveryone.SharedViewModel
import com.shabinder.musicForEveryone.databinding.MainFragmentBinding import com.shabinder.musicForEveryone.databinding.MainFragmentBinding
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper
import com.shabinder.musicForEveryone.models.Track
import com.shabinder.musicForEveryone.recyclerView.TrackListAdapter import com.shabinder.musicForEveryone.recyclerView.TrackListAdapter
import com.shabinder.musicForEveryone.utils.SpotifyService
import com.shabinder.musicForEveryone.utils.bindImage import com.shabinder.musicForEveryone.utils.bindImage
import kaaes.spotify.webapi.android.SpotifyService import kotlinx.coroutines.Dispatchers
import kaaes.spotify.webapi.android.models.Track
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
@Suppress("DEPRECATION")
class MainFragment : Fragment(),DownloadHelper { class MainFragment : Fragment(),DownloadHelper {
lateinit var binding:MainFragmentBinding private lateinit var binding:MainFragmentBinding
private lateinit var mainViewModel: MainViewModel
private lateinit var sharedViewModel: SharedViewModel private lateinit var sharedViewModel: SharedViewModel
var spotify : SpotifyService? = null private lateinit var adapter:TrackListAdapter
var type:String = "" private var spotifyService : SpotifyService? = null
var spotifyLink = "" private var type:String = ""
private var spotifyLink = ""
private var i: Intent? = null
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, inflater: LayoutInflater, container: ViewGroup?,
savedInstanceState: Bundle? savedInstanceState: Bundle?
@ -34,111 +46,211 @@ class MainFragment : Fragment(),DownloadHelper {
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) 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 { binding.btnSearch.setOnClickListener {
spotifyLink = binding.spotifyLink.text.toString() sharedViewModel.isConnected.value = isOnline()
spotifyLink = binding.linkSearch.text.toString()
val link = spotifyLink.substringAfterLast('/' , "Error").substringBefore('?') val link = spotifyLink.substringAfterLast('/', "Error").substringBefore('?')
type = spotifyLink.substringBeforeLast('/' , "Error").substringAfterLast('/') type = spotifyLink.substringBeforeLast('/', "Error").substringAfterLast('/')
Log.i("Fragment", "$type : $link") Log.i("Fragment", "$type : $link")
val adapter = TrackListAdapter() if (type == "Error" || link == "Error") {
binding.trackList.adapter = adapter showToast("Please Check Your Link!")
adapter.sharedViewModel = sharedViewModel } else if(sharedViewModel.isConnected.value == false){
when(type){ sharedViewModel.showAlertDialog(resources,requireContext())
"track" -> { }
sharedViewModel.uiScope.launch{ else {
val trackObject = sharedViewModel.getTrackDetails(link) 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>() binding.btnDownloadAll.setOnClickListener {
trackList.add(trackObject!!) sharedViewModel.uiScope.launch {
bindImage(binding.imageView, trackObject.album.images[0].url) withContext(Dispatchers.IO) {
adapter.totalItems = 1 downloadAllTracks(
adapter.trackList = trackList trackList,
adapter.notifyDataSetChanged() sharedViewModel.ytDownloader,
Log.i("Adapter",trackList.size.toString()) sharedViewModel.downloadManager
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) } )
}
}
}
}
}
"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) handleIntent()
sharedViewModel.userName.observe(viewLifecycleOwner, Observer { if(savedInstanceState != null && binding.linkSearch.text.toString() != ""){
//Waiting for Authentication to Finish with Spotify binding.linkSearch.setText(savedInstanceState["searchLink"].toString())
if (it != "Placeholder"){ binding.btnSearch.performClick()
if(sharedViewModel.intentString != ""){binding.btnSearch.performClick()} setUiVisibility()
} }
})
return binding.root return binding.root
} }
private fun downloadAllTracks(trackList : List<Track>) { private fun openSpotifyButton() {
sharedViewModel.uiScope.launch { val manager: PackageManager = requireActivity().packageManager
trackList.forEach { downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${it.name} ${it.artists[0].name?:""}") } 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() 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.R
import com.shabinder.musicForEveryone.SharedViewModel import com.shabinder.musicForEveryone.SharedViewModel
import com.shabinder.musicForEveryone.downloadHelper.DownloadHelper 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 com.shabinder.musicForEveryone.utils.bindImage
import kaaes.spotify.webapi.android.models.Track
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),DownloadHelper { class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),DownloadHelper {
@ -19,6 +20,8 @@ class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),Downl
var trackList = listOf<Track>() var trackList = listOf<Track>()
var totalItems:Int = 0 var totalItems:Int = 0
var sharedViewModel = SharedViewModel() var sharedViewModel = SharedViewModel()
var isAlbum:Boolean = false
var mainFragment:MainFragment? = null
override fun getItemCount():Int = totalItems override fun getItemCount():Int = totalItems
@ -31,16 +34,16 @@ class TrackListAdapter:RecyclerView.Adapter<TrackListAdapter.ViewHolder>(),Downl
override fun onBindViewHolder(holder: ViewHolder, position: Int) { override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val item = trackList[position] val item = trackList[position]
if(totalItems == 1){holder.coverImage.visibility = View.GONE}else{ if(totalItems == 1 || isAlbum){holder.coverImage.visibility = View.GONE}else{
bindImage(holder.coverImage,item.album.images[0].url) 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.trackName.text = "${if(item.name!!.length > 17){"${item.name!!.subSequence(0,16)}..."}else{item.name}}"
holder.artistName.text = "${item.artists[0]?.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.duration.text = "${item.duration_ms/1000/60} minutes, ${(item.duration_ms/1000)%60} sec"
holder.downloadBtn.setOnClickListener{ holder.downloadBtn.setOnClickListener{
sharedViewModel.uiScope.launch { 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.bumptech.glide.request.RequestOptions
import com.shabinder.musicForEveryone.R import com.shabinder.musicForEveryone.R
@BindingAdapter("imageUrl") @BindingAdapter("imageUrl")
fun bindImage(imgView: ImageView, imgUrl: String?) { fun bindImage(imgView: ImageView, imgUrl: String?) {
imgUrl?.let {
imgUrl?.let { val imgUri = imgUrl.toUri().buildUpon().scheme("https").build()
val imgUri = imgUrl.toUri().buildUpon().scheme("https").build() Glide.with(imgView.context)
Glide.with(imgView.context) .load(imgUri)
.load(imgUri) .apply(RequestOptions()
.apply(RequestOptions() .placeholder(R.drawable.ic_song_placeholder)
.error(R.drawable.ic_musicplaceholder)) .error(R.drawable.ic_musicplaceholder))
.into(imgView) .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 { object YoutubeInterface {
private var youtube: YouTube? = null private var youtube: YouTube? = null
private var query:YouTube.Search.List? = null private var query:YouTube.Search.List? = null
var apiKey:String = "AIzaSyDuRmMA_2mF56BjlhhNpa0SIbjMgjjFaEI" private var apiKey:String = "AIzaSyDuRmMA_2mF56BjlhhNpa0SIbjMgjjFaEI"
var apiKey2:String = "AIzaSyCotyqgqmz5qw4-IH0tiezIrIIDHLI2yNs" private var apiKey2:String = "AIzaSyCotyqgqmz5qw4-IH0tiezIrIIDHLI2yNs"
var clientID : String = "1040727735015-er2mvvljt45cabkuqimsh3iabqvfpvms.apps.googleusercontent.com"
fun youtubeConnector() { fun youtubeConnector() {
youtube = youtube =