Skip to content

Commit

Permalink
Merge pull request #5 from BobbyESP/downloader_impl
Browse files Browse the repository at this point in the history
Implementation of song searching.
  • Loading branch information
BobbyESP authored Nov 19, 2022
2 parents 211e90d + 25fd511 commit a447169
Show file tree
Hide file tree
Showing 30 changed files with 1,147 additions and 89 deletions.
50 changes: 45 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ plugins {
apply(plugin = "dagger.hilt.android.plugin")

val versionMajor = 0
val versionMinor = 1
val versionPatch = 3
val versionMinor = 2
val versionPatch = 0
val versionBuild = 0
val isStable = true

Expand All @@ -22,9 +22,11 @@ val navigationVersion: String by rootProject.extra
val roomVersion: String by rootProject.extra
val accompanistVersion: String by rootProject.extra
val composeMd3Version: String by rootProject.extra
val youtubedlAndroidVersion: String by rootProject.extra
val coilVersion: String by rootProject.extra
val okhttpVersion: String by rootProject.extra
val hiltVersion: String by rootProject.extra
val spotifyLibrary: String by rootProject.extra

val keystorePropertiesFile = rootProject.file("keystore.properties")

Expand Down Expand Up @@ -86,10 +88,43 @@ android {
)
if (keystorePropertiesFile.exists())
signingConfig = signingConfigs.getByName("debug")
buildConfigField("String",
"SPOTIFY_CLIENT_ID",
"\"abcad8ba647d4b0ebae797a8f444ac9b\"")
buildConfigField("String",
"SPOTIFY_REDIRECT_URI",
"\"spowlo://spotify-auth\"")
buildConfigField(
"String",
"SPOTIFY_REDIRECT_URI_PKCE",
"\"spowlo://spotify-pkce\""
)
packagingOptions {
resources.excludes.add("META-INF/*.kotlin_module")
}
matchingFallbacks.add(0, "debug")
matchingFallbacks.add(1, "release")
}
debug {
if (keystorePropertiesFile.exists())
signingConfig = signingConfigs.getByName("debug")
buildConfigField(
"String",
"SPOTIFY_CLIENT_ID",
"\"abcad8ba647d4b0ebae797a8f444ac9b\"")

buildConfigField(
"String",
"SPOTIFY_REDIRECT_URI_AUTH",
"\"spowlo://spotify-auth\"")

buildConfigField(
"String",
"SPOTIFY_REDIRECT_URI_PKCE",
"\"spowlo://spotify-pkce\""
)
matchingFallbacks.add(0, "debug")
matchingFallbacks.add(1, "release")
}
}
compileOptions {
Expand Down Expand Up @@ -165,11 +200,11 @@ dependencies {
kapt("androidx.hilt:hilt-compiler:1.0.0")
implementation("com.google.dagger:hilt-android:$hiltVersion")
kapt("com.google.dagger:hilt-android-compiler:$hiltVersion")
//Room (Databases)

/*implementation("androidx.room:room-runtime:$roomVersion")
//Room (Databases)
implementation("androidx.room:room-runtime:$roomVersion")
implementation("androidx.room:room-ktx:$roomVersion")
kapt("androidx.room:room-compiler:$roomVersion")*/
kapt("androidx.room:room-compiler:$roomVersion")

// Retrofit and okhttp
implementation("com.squareup.retrofit2:retrofit:2.9.0")
Expand All @@ -184,6 +219,11 @@ dependencies {
//SimpleStorage (SAF Simplifier)
implementation("com.anggrayudi:storage:1.5.0")

//Yt-dlp

//Spotify SDK Integration library
implementation("com.adamratzman:spotify-api-kotlin-core:$spotifyLibrary")

//Unit testing
testImplementation("junit:junit:4.13.2")
testImplementation("junit:junit:4.13.2")
Expand Down
22 changes: 22 additions & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
android:name=".Spowlo"
android:allowBackup="true"
android:extractNativeLibs="true"
android:requestLegacyExternalStorage="true"
android:icon="@drawable/spowlo_icon"
android:label="@string/app_name"
android:roundIcon="@drawable/spowlo_icon"
Expand Down Expand Up @@ -64,6 +65,27 @@
android:name="autoStoreLocales"
android:value="true" />
</service>
<activity
android:name="com.spotify.sdk.android.auth.LoginActivity"
android:theme="@android:style/Theme.Translucent.NoTitleBar"
android:exported="true">
<intent-filter>
<data android:scheme="spowlo" android:host="spotify-auth" />
</intent-filter>
</activity>

<activity android:name=".domain.spotify.web_api.auth.SpotifyPkceLoginActivityImpl"
android:launchMode="singleTop" android:exported="true">
<intent-filter>
<action android:name="android.intent.action.VIEW" />

<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />

<data android:scheme="spowlo" android:host="spotify-pkce" />
</intent-filter>
</activity>

</application>

</manifest>
41 changes: 40 additions & 1 deletion app/src/main/java/com/bobbyesp/spowlo/Spowlo.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,27 +2,66 @@ package com.bobbyesp.spowlo

import android.annotation.SuppressLint
import android.app.Application
import android.content.ClipboardManager
import android.content.Context
import com.anggrayudi.storage.SimpleStorageHelper
import android.net.ConnectivityManager
import com.bobbyesp.spowlo.data.auth.AuthModel
import com.bobbyesp.spowlo.database.CommandTemplate
import com.bobbyesp.spowlo.util.DatabaseUtil
import com.bobbyesp.spowlo.util.PreferencesUtil
import com.bobbyesp.spowlo.util.PreferencesUtil.AUDIO_DIRECTORY
import com.bobbyesp.spowlo.util.PreferencesUtil.TEMPLATE_INDEX
import com.google.android.material.color.DynamicColors
import com.tencent.mmkv.MMKV
import dagger.hilt.android.HiltAndroidApp
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch

@HiltAndroidApp
class Spowlo : Application() {
lateinit var model: AuthModel

override fun onCreate() {
super.onCreate()
MMKV.initialize(this)
context = applicationContext
this.model = AuthModel
applicationScope = CoroutineScope(SupervisorJob())
DynamicColors.applyToActivitiesIfAvailable(this)

clipboard = getSystemService(ClipboardManager::class.java)
connectivityManager = getSystemService(ConnectivityManager::class.java)

applicationScope.launch((Dispatchers.IO)) {
if (!PreferencesUtil.containsKey(TEMPLATE_INDEX)) {
PreferencesUtil.updateInt(TEMPLATE_INDEX, 0)
DatabaseUtil.insertTemplate(
CommandTemplate(
0,
context.getString(R.string.custom_command_template),
PreferencesUtil.getString(
PreferencesUtil.TEMPLATE, context.getString(R.string.template_example)
)
)
)
}
}
}

companion object{
private const val TAG = "Spowlo"
lateinit var applicationScope: CoroutineScope
lateinit var clipboard: ClipboardManager
lateinit var audioDownloadDir: String
var ytdlpVersion = ""
lateinit var connectivityManager: ConnectivityManager

fun updateDownloadDir(path: String) {
audioDownloadDir = path
PreferencesUtil.updateString(AUDIO_DIRECTORY, path)
}

@SuppressLint("StaticFieldLeak")
lateinit var context: Context
Expand Down
15 changes: 15 additions & 0 deletions app/src/main/java/com/bobbyesp/spowlo/data/auth/AuthModel.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.bobbyesp.spowlo.data.auth

import com.adamratzman.spotify.auth.SpotifyDefaultCredentialStore
import com.bobbyesp.spowlo.BuildConfig
import com.bobbyesp.spowlo.Spowlo

object AuthModel {
val credentialStore by lazy {
SpotifyDefaultCredentialStore(
clientId = BuildConfig.SPOTIFY_CLIENT_ID,
redirectUri = BuildConfig.SPOTIFY_REDIRECT_URI_PKCE,
applicationContext = Spowlo.context
)
}
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/bobbyesp/spowlo/database/AppDatabase.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bobbyesp.spowlo.database

import androidx.room.Database
import androidx.room.RoomDatabase

@Database(
entities = [DownloadedSongInfo::class, CommandTemplate::class], version = 3,
exportSchema = false
)
abstract class AppDatabase : RoomDatabase() {
abstract fun songInfoDao(): SongInfoDao
}
12 changes: 12 additions & 0 deletions app/src/main/java/com/bobbyesp/spowlo/database/CommandTemplate.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.bobbyesp.spowlo.database

import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity
@kotlinx.serialization.Serializable
data class CommandTemplate(
@PrimaryKey(autoGenerate = true) val id: Int,
val name: String,
val template: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.bobbyesp.spowlo.database

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey

@Entity(tableName = "DownloadedSongInfo")
data class DownloadedSongInfo(
@PrimaryKey(autoGenerate = true) val id: Int,
val songTitle: String,
val songArtist: String,
val songUrl: String,
val thumbnailUrl: String,
val songPath: String,
@ColumnInfo(defaultValue = "Unknown")
val extractor: String = "Unknown"
)
43 changes: 43 additions & 0 deletions app/src/main/java/com/bobbyesp/spowlo/database/SongInfoDao.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package com.bobbyesp.spowlo.database

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface SongInfoDao {
@Insert
suspend fun insertAll(vararg info: DownloadedSongInfo)

@Query("select * from DownloadedSongInfo")
fun getAllMedia(): Flow<List<DownloadedSongInfo>>

@Query("select * from DownloadedSongInfo where id=:id")
suspend fun getInfoById(id: Int): DownloadedSongInfo

@Delete
suspend fun delete(info: DownloadedSongInfo)

@Query("DELETE FROM DownloadedSongInfo WHERE id = :id")
suspend fun deleteInfoById(id: Int)

@Query("DELETE FROM DownloadedSongInfo WHERE songPath = :path")
suspend fun deleteInfoByPath(path: String)

@Query("SELECT * FROM CommandTemplate")
fun getTemplateFlow(): Flow<List<CommandTemplate>>

@Query("SELECT * FROM CommandTemplate")
suspend fun getTemplateList(): List<CommandTemplate>

@Insert
suspend fun insertTemplate(template: CommandTemplate)

@Update
suspend fun updateTemplate(template: CommandTemplate)

@Delete
suspend fun deleteTemplate(template: CommandTemplate)

@Query("SELECT * FROM CommandTemplate where id = :id")
suspend fun getTemplateById(id: Int): CommandTemplate
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.bobbyesp.spowlo.domain.spotify.web_api.auth

import android.app.Activity
import com.adamratzman.spotify.SpotifyClientApi
import com.adamratzman.spotify.SpotifyScope
import com.adamratzman.spotify.auth.pkce.AbstractSpotifyPkceLoginActivity
import com.bobbyesp.spowlo.BuildConfig
import com.bobbyesp.spowlo.Spowlo
import com.bobbyesp.spowlo.presentation.MainActivity
import com.bobbyesp.spowlo.util.Utils.makeToast

internal var pkceClassBackTo: Class<out Activity>? = null

class SpotifyPkceLoginActivityImpl: AbstractSpotifyPkceLoginActivity() {
override val clientId = BuildConfig.SPOTIFY_CLIENT_ID
override val redirectUri = BuildConfig.SPOTIFY_REDIRECT_URI_PKCE
override val scopes = SpotifyScope.values().toList()

override fun onFailure(exception: Exception) {
exception.printStackTrace()
pkceClassBackTo = null
makeToast("Auth failed: ${exception.message}")
}

override fun onSuccess(api: SpotifyClientApi) {
val model = (application as Spowlo).model
model.credentialStore.setSpotifyApi(api)
val classBackTo = pkceClassBackTo ?: MainActivity::class.java
pkceClassBackTo = null
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package com.bobbyesp.spowlo.domain.spotify.web_api.utilities

import android.app.Activity
import com.adamratzman.spotify.SpotifyClientApi
import com.adamratzman.spotify.SpotifyException
import com.adamratzman.spotify.auth.pkce.startSpotifyClientPkceLoginActivity
import com.bobbyesp.spowlo.data.auth.AuthModel
import com.bobbyesp.spowlo.domain.spotify.web_api.auth.SpotifyPkceLoginActivityImpl
import com.bobbyesp.spowlo.domain.spotify.web_api.auth.pkceClassBackTo
import kotlinx.coroutines.runBlocking

fun <T> Activity.guardValidSpotifyApi(
classBackTo: Class<out Activity>? = null,
alreadyTriedToReauthenticate: Boolean = false,
block: suspend (api: SpotifyClientApi) -> T
): T? {
return runBlocking {
try {
val api = AuthModel.credentialStore.getSpotifyClientPkceApi() ?: throw SpotifyException.ReAuthenticationNeededException()
block(api)
} catch (e: SpotifyException) {
e.printStackTrace()
val api = AuthModel.credentialStore.getSpotifyClientPkceApi()!!
if (!alreadyTriedToReauthenticate) {
try {
api.refreshToken()
AuthModel.credentialStore.spotifyToken = api.token
block(api)
} catch (e: SpotifyException.ReAuthenticationNeededException) {
e.printStackTrace()
return@runBlocking guardValidSpotifyApi(
classBackTo = classBackTo,
alreadyTriedToReauthenticate = true,
block = block
)
} catch (e: IllegalArgumentException) {
e.printStackTrace()
return@runBlocking guardValidSpotifyApi(
classBackTo = classBackTo,
alreadyTriedToReauthenticate = true,
block = block
)
}
} else {
pkceClassBackTo = classBackTo
startSpotifyClientPkceLoginActivity(SpotifyPkceLoginActivityImpl::class.java)
null
}
}
}
}
Loading

0 comments on commit a447169

Please sign in to comment.