Added Gradients in App Ui & Added Download All Tracks Functionality

This commit is contained in:
shabinder 2020-07-22 21:30:42 +05:30
parent 735f7c270e
commit 0ce929df9b
10 changed files with 175 additions and 114 deletions

View File

@ -36,6 +36,10 @@
android:name="com.spotify.sdk.android.authentication.LoginActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar" />
<meta-data
android:name="preloaded_fonts"
android:resource="@array/preloaded_fonts" />
</application>
</manifest>

View File

@ -104,8 +104,8 @@ class MainActivity : AppCompatActivity() ,DownloadHelper{
sharedViewModel.uiScope.launch {
val me = spotifyExtra?.getMe()?.display_name
sharedViewModel.userName.value = me
Log.i("Network",me!!)
sharedViewModel.userName.value = "Logged in as: $me"
Log.i("Network","Hello, " + me!!)
}
sharedViewModel.userName.observe(this, Observer {

View File

@ -14,43 +14,49 @@ import kotlinx.coroutines.withContext
import java.io.File
interface DownloadHelper {
suspend fun downloadTrack(ytDownloader: YoutubeDownloader?, downloadManager: DownloadManager?, searchQuery:String){
suspend fun downloadTrack(
ytDownloader: YoutubeDownloader?,
downloadManager: DownloadManager?,
searchQuery: String
) {
withContext(Dispatchers.IO){
val downloadIdList = mutableListOf<Int>()
val data = YoutubeInterface.search(searchQuery)?.get(0)
if (data==null){Log.i("DownloadHelper","Youtube Request Failed!")}else{
withContext(Dispatchers.IO) {
val data = YoutubeInterface.search(searchQuery)?.get(0)
if (data == null) {
Log.i("DownloadHelper", "Youtube Request Failed!")
} else {
val video = ytDownloader?.getVideo(data.id)
//Fetching a Video Object.
val details = video?.details()
try{
val format: Format =
video?.findAudioWithQuality(AudioQuality.medium)?.get(0) as Format
val audioUrl = format.url()
Log.i("DHelper Link Found", audioUrl)
if (audioUrl != null) {
downloadFile(audioUrl, downloadManager, details!!.title())
} else {
Log.i("YT audio url is null", format.toString())
}
}catch (e:ArrayIndexOutOfBoundsException){
Log.i("Catch",e.toString())
}
val format:Format = video?.findAudioWithQuality(AudioQuality.low)?.get(0) as Format
val audioUrl = format.url()
if (audioUrl != null) {
downloadFile(audioUrl,downloadManager,details!!.title())
Log.i("DHelper Start Download", audioUrl)
}else{Log.i("YT audio url is null", format.toString())}
}
// Library Inbuilt function to Save File (Need Scoped Storage Implementation)
// val file: File = video.download( format , outputDir)
}
//@data = 1st object from YT query.
}
/**
* Downloading Using Android Download Manager
* */
suspend fun downloadFile(url: String, downloadManager: DownloadManager?,title:String){
withContext(Dispatchers.IO){
suspend fun downloadFile(url: String, downloadManager: DownloadManager?, title: String) {
withContext(Dispatchers.IO) {
val audioUri = Uri.parse(url)
val outputDir = File.separator + "Spotify-Downloads" +File.separator + "${removeIllegalChars(title)}.mp3"
val outputDir =
File.separator + "Spotify-Downloads" + File.separator + "${removeIllegalChars(title)}.mp3"
val request = DownloadManager.Request(audioUri)
.setAllowedNetworkTypes(
@ -60,10 +66,10 @@ interface DownloadHelper {
.setAllowedOverRoaming(false)
.setTitle(title)
.setDescription("Spotify Downloader Working Up here...")
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC,outputDir)
.setDestinationInExternalPublicDir(Environment.DIRECTORY_MUSIC, outputDir)
.setNotificationVisibility(VISIBILITY_VISIBLE_NOTIFY_COMPLETED)
downloadManager?.enqueue(request)
Log.i("DownloadManager","Download Request Sent")
Log.i("DownloadManager", "Download Request Sent")
}
}

View File

@ -61,51 +61,56 @@ class MainFragment : Fragment(),DownloadHelper {
adapter.trackList = trackList
adapter.notifyDataSetChanged()
Log.i("Adapter",trackList.size.toString())
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) }
}
}
"album" -> {
sharedViewModel.uiScope.launch{
val albumObject = sharedViewModel.getAlbumDetails(link)
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()
binding.titleView.text = albumObject!!.name
binding.titleView.visibility =View.VISIBLE
binding.imageView.visibility =View.VISIBLE
Log.i("Adapter",trackList.size.toString())
val trackList = mutableListOf<Track>()
albumObject.tracks?.items?.forEach { trackList.add(it as Track) }
adapter.totalItems = trackList.size
adapter.trackList = trackList
adapter.notifyDataSetChanged()
bindImage(binding.imageView, albumObject.images[0].url)
binding.btnDownloadAll.setOnClickListener { downloadAllTracks(trackList) }
Log.i("Adapter",trackList.size.toString())
bindImage(binding.imageView, albumObject.images[0].url)
}
}
}
"playlist" -> {
sharedViewModel.uiScope.launch{
val playlistObject = sharedViewModel.getPlaylistDetails(link)
binding.titleView.text = "${if(playlistObject!!.name.length > 18){"${playlistObject.name.subSequence(0,17)}..."}else{playlistObject.name}}"
binding.titleView.visibility =View.VISIBLE
binding.imageView.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())
val playlistObject = sharedViewModel.getPlaylistDetails(link)
binding.btnDownloadAll.visibility =View.VISIBLE
bindImage(binding.imageView, playlistObject.images[0].url)
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) }
}
@ -127,6 +132,12 @@ class MainFragment : Fragment(),DownloadHelper {
return binding.root
}
private fun downloadAllTracks(trackList : List<Track>) {
sharedViewModel.uiScope.launch {
trackList.forEach { downloadTrack(sharedViewModel.ytDownloader,sharedViewModel.downloadManager,"${it.name} ${it.artists[0].name?:""}") }
}
}
private fun showToast(message:String){
Toast.makeText(context,message,Toast.LENGTH_SHORT).show()
}

View File

@ -11,6 +11,8 @@ 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"
fun youtubeConnector() {
@ -23,14 +25,14 @@ object YoutubeInterface {
query?.maxResults = 1
query?.type = "video"
query?.fields =
"items(id/kind,id/videoId,snippet/title,snippet/description,snippet/thumbnails/default/url)"
"items(id/videoId,snippet/title,snippet/thumbnails/default/url)"
} catch (e: IOException) {
Log.i("YC", "Could not initialize: $e")
Log.i("YI", "Could not initialize: $e")
}
}
fun search(keywords: String?): List<VideoItem>? {
Log.i("YC searched for",keywords.toString())
Log.i("YI searched for",keywords.toString())
if (youtube == null){youtubeConnector()}
query!!.q= keywords
return try {
@ -42,23 +44,25 @@ object YoutubeInterface {
val item = VideoItem(
id = result.id.videoId,
title = result.snippet.title,
description = result.snippet.description,
// description = result.snippet.description,
thumbnailUrl = result.snippet.thumbnails.default.url
)
items.add(item)
Log.i("YC links received",item.id)
Log.i("YI links received",item.id)
}
items
} catch (e: IOException) {
Log.d("YC", "Could not search: $e")
null
Log.d("YI", "Could not search: $e")
if(query?.key == apiKey2){return null}
query?.key = apiKey2
search(keywords)
}
}
data class VideoItem(
val id:String,
val title:String,
val description: String,
// val description: String,
val thumbnailUrl:String
)

View File

@ -12,12 +12,12 @@
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginBottom="2dp"
android:background="@drawable/text_background"
android:background="@drawable/text_background_accented"
android:padding="5dp"
android:paddingTop="6dp"
android:text="MainFragment"
android:text="Authentication Needed"
android:textColor="@color/colorPrimary"
android:textSize="12dp"
android:textSize="10dp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"

View File

@ -10,64 +10,96 @@
android:fitsSystemWindows="true"
tools:context=".fragments.MainFragment">
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_download_all"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:background="@drawable/btn_design"
android:drawableEnd="@drawable/ic_arrow_slim"
android:drawablePadding="4dp"
android:drawableTint="@color/black"
android:padding="12dp"
android:text="Download All |"
android:textColor="@color/black"
android:textSize="16sp"
android:visibility="visible"
app:layout_anchor="@+id/constraint_layout"
app:layout_anchorGravity="top|center" />
<com.google.android.material.appbar.AppBarLayout
android:id="@+id/appbar"
android:layout_width="match_parent"
android:layout_height="230dp">
android:layout_height="280dp">
<com.google.android.material.appbar.CollapsingToolbarLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
app:contentScrim="?attr/colorPrimary"
app:contentScrim="#CE6A40FF"
app:layout_scrollFlags="scroll|enterAlways|enterAlwaysCollapsed"
app:layout_scrollInterpolator="@android:anim/decelerate_interpolator"
app:toolbarId="@+id/toolbar">
<LinearLayout
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/constraintLayout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal">
android:layout_height="match_parent">
<EditText
android:id="@+id/spotifyLink"
android:layout_width="257dp"
android:layout_height="match_parent"
android:layout_marginStart="8dp"
android:layout_width="wrap_content"
android:layout_height="48dp"
android:layout_marginStart="16dp"
android:layout_marginTop="8dp"
android:layout_marginEnd="20dp"
android:background="@drawable/text_background"
android:layout_marginBottom="8dp"
android:background="@drawable/text_background_accented"
android:ems="10"
android:hint="Link From Spotify"
android:inputType="text"
android:padding="5dp"
android:padding="8dp"
android:textAlignment="center"
android:textColor="@color/white"
android:textColorHint="@color/grey"
android:textSize="19sp" />
android:textSize="19sp"
app:layout_constraintBottom_toTopOf="@+id/image_view"
app:layout_constraintEnd_toStartOf="@+id/btn_search"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<Button
<androidx.appcompat.widget.AppCompatButton
android:id="@+id/btn_search"
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:backgroundTint="@color/colorPrimary"
android:layout_height="44dp"
android:layout_marginEnd="16dp"
android:background="@drawable/btn_design"
android:paddingLeft="4dp"
android:paddingRight="4dp"
android:text="Search"
android:textSize="18sp" />
</LinearLayout>
<ImageView
android:id="@+id/image_view"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_marginTop="8dp"
android:contentDescription="Album Cover"
android:scaleType="centerInside"
android:src="@drawable/ic_launcher_foreground"
app:layout_collapseMode="parallax"/>
</LinearLayout>
android:textColor="@color/black"
android:textSize="16sp"
app:layout_constraintBottom_toBottomOf="@id/spotifyLink"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@+id/spotifyLink"
app:layout_constraintTop_toTopOf="@id/spotifyLink" />
<ImageView
android:id="@+id/image_view"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="6dp"
android:contentDescription="Album Cover"
android:foreground="@drawable/gradient"
android:padding="20dp"
android:paddingBottom="35dp"
android:src="@drawable/ic_launcher_foreground"
android:visibility="gone"
app:layout_collapseMode="parallax"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/btn_search" />
</androidx.constraintlayout.widget.ConstraintLayout>
</com.google.android.material.appbar.CollapsingToolbarLayout>
</com.google.android.material.appbar.AppBarLayout>
@ -113,7 +145,7 @@
android:id="@+id/track_list"
android:layout_width="0dp"
android:layout_height="0dp"
android:layout_marginTop="12dp"
android:layout_marginTop="22dp"
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
app:layout_behavior="com.google.android.material.appbar.AppBarLayout$ScrollingViewBehavior"
app:layout_constraintBottom_toBottomOf="parent"
@ -121,7 +153,7 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/title_view" />
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</androidx.coordinatorlayout.widget.CoordinatorLayout>

View File

@ -28,17 +28,16 @@
android:id="@+id/track_name"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="8dp"
android:fontFamily="monospace"
android:letterSpacing="-0.03"
android:layout_marginStart="8dp"
android:layout_marginTop="14dp"
android:fontFamily="@font/raleway_semibold"
android:letterSpacing="0.04"
android:lines="1"
android:text="The Spectre"
android:textAllCaps="false"
android:textAppearance="@style/TextAppearance.AppTheme.Headline4"
android:textColor="@color/colorPrimary"
android:textSize="18sp"
android:textStyle="bold"
android:textColor="#9AB3FF"
android:textSize="20sp"
app:layout_constraintEnd_toStartOf="@+id/btn_download"
app:layout_constraintStart_toStartOf="@+id/artist"
app:layout_constraintTop_toTopOf="parent" />
@ -49,9 +48,11 @@
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="12dp"
android:layout_marginTop="12dp"
android:layout_marginTop="8dp"
android:layout_marginBottom="8dp"
android:paddingLeft="9dp"
android:text="Alan Walker"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@+id/duration"
app:layout_constraintStart_toEndOf="@+id/imageUrl"
@ -63,8 +64,10 @@
style="@style/TextAppearance.AppCompat.Body2"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginEnd="3dp"
android:paddingLeft="9dp"
android:text="4 minutes, 20 sec"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="@+id/artist"
app:layout_constraintEnd_toStartOf="@+id/btn_download"
app:layout_constraintStart_toEndOf="@+id/artist"

View File

@ -1,8 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="colorPrimary">#0AFF02</color>
<color name="colorPrimaryDark">#43FF56</color>
<color name="colorAccent">#43FF56</color>
<color name="colorPrimary">#FC5C7D</color>
<color name="colorPrimaryDark">#CE1CFF</color>
<color name="colorAccent">#8497FA</color>
<color name="white">#FFFFFF</color>
<color name="grey">#99FFFFFF</color>
<color name="black">#000000</color>

View File

@ -2,12 +2,12 @@
<!-- Base application theme. -->
<style name="AppTheme" parent="Theme.MaterialComponents.DayNight.NoActionBar">
<!-- Customize your theme here. -->
<item name="colorPrimaryDark">#00730C</item>
<item name="colorPrimary">#05A300</item>
<item name="colorPrimaryDark">#000000</item>
<item name="colorPrimary">#FC5C7D</item>
<item name="android:background">#000000</item>
<item name="android:textColor">#FFFFFF</item>
<item name="colorAccent">#43FF56</item>
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#6DFF7C</item>
<item name="colorAccent">#6A82FB</item>
<item name="android:outlineAmbientShadowColor" tools:targetApi="p">#A9B200FF</item>
<item name="android:radius">11dp</item>
<!-- Text Appearances !-->
<!-- use our brand's custom TextAppearance4 !-->