diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 64d7de01..71fcd374 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -26,9 +26,14 @@ + + + + - diff --git a/app/src/main/ic_launcher-playstore.png b/app/src/main/ic_launcher-playstore.png new file mode 100644 index 00000000..d6cd1525 Binary files /dev/null and b/app/src/main/ic_launcher-playstore.png differ diff --git a/app/src/main/java/com/shabinder/musicForEveryone/MainActivity.kt b/app/src/main/java/com/shabinder/musicForEveryone/MainActivity.kt index 0789fe4b..26d2db73 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/MainActivity.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/MainActivity.kt @@ -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") + } + } + } + } +*/ } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/SharedViewModel.kt b/app/src/main/java/com/shabinder/musicForEveryone/SharedViewModel.kt index a25b9dbb..031ed597 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/SharedViewModel.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/SharedViewModel.kt @@ -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().apply { value = "Placeholder" } - var spotify :SpotifyService? = null - var spotifyExtra : SpotifyNewService? = null + var accessToken = MutableLiveData().apply { value = "" } + var spotifyService : SpotifyService? = null var ytDownloader : YoutubeDownloader? = null var downloadManager : DownloadManager? = null + var isConnected = MutableLiveData().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() + } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/downloadHelper/DownloadHelper.kt b/app/src/main/java/com/shabinder/musicForEveryone/downloadHelper/DownloadHelper.kt index 13126423..9fb04cdc 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/downloadHelper/DownloadHelper.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/downloadHelper/DownloadHelper.kt @@ -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, 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") + } } diff --git a/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainFragment.kt b/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainFragment.kt index 3fc56281..f59a1c3a 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainFragment.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainFragment.kt @@ -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() + trackList.add(trackObject!!) + mainViewModel.trackList = trackList + mainViewModel.coverUrl = trackObject.album!!.images?.get(0)!!.url!! + bindImage(binding.imageView,mainViewModel.coverUrl) + adapterConfig(trackList) - val trackList = mutableListOf() - 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() + 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() + 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() - 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() - 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) { - 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){ + 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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainViewModel.kt b/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainViewModel.kt new file mode 100644 index 00000000..0974b9d9 --- /dev/null +++ b/app/src/main/java/com/shabinder/musicForEveryone/fragments/MainViewModel.kt @@ -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() + var coverUrl:String = "" + +} \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/recyclerView/TrackListAdapter.kt b/app/src/main/java/com/shabinder/musicForEveryone/recyclerView/TrackListAdapter.kt index e89b4f58..65a0cb58 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/recyclerView/TrackListAdapter.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/recyclerView/TrackListAdapter.kt @@ -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(),DownloadHelper { @@ -19,6 +20,8 @@ class TrackListAdapter:RecyclerView.Adapter(),Downl var trackList = listOf() 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(),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?:""}") } } diff --git a/app/src/main/java/com/shabinder/musicForEveryone/utils/BindingAdapter.kt b/app/src/main/java/com/shabinder/musicForEveryone/utils/BindingAdapter.kt index 072b990c..04b7d209 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/utils/BindingAdapter.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/utils/BindingAdapter.kt @@ -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) + } } \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyNewService.kt b/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyNewService.kt deleted file mode 100644 index 05214aee..00000000 --- a/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyNewService.kt +++ /dev/null @@ -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? = null, - var followers: Followers? = null, - var href: String? = null, - var id: String? = null, - var images: List? = null, - var product:String, - var type: String? = null, - var uri: String? = null) \ No newline at end of file diff --git a/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyService.kt b/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyService.kt new file mode 100644 index 00000000..bbe572cd --- /dev/null +++ b/app/src/main/java/com/shabinder/musicForEveryone/utils/SpotifyService.kt @@ -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? + +} diff --git a/app/src/main/java/com/shabinder/musicForEveryone/utils/YoutubeInterface.kt b/app/src/main/java/com/shabinder/musicForEveryone/utils/YoutubeInterface.kt index 6fccea55..ba1e9955 100644 --- a/app/src/main/java/com/shabinder/musicForEveryone/utils/YoutubeInterface.kt +++ b/app/src/main/java/com/shabinder/musicForEveryone/utils/YoutubeInterface.kt @@ -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 =