diff --git a/android/src/main/java/com/shabinder/android/MainActivity.kt b/android/src/main/java/com/shabinder/android/MainActivity.kt index e5e0e9b7..9e737533 100644 --- a/android/src/main/java/com/shabinder/android/MainActivity.kt +++ b/android/src/main/java/com/shabinder/android/MainActivity.kt @@ -25,9 +25,12 @@ import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.extensions.compose.jetbrains.rootComponent import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory +import com.razorpay.Checkout +import com.razorpay.PaymentResultListener import com.shabinder.android.utils.checkIfLatestVersion import com.shabinder.android.utils.disableDozeMode import com.shabinder.android.utils.requestStoragePermission +import com.shabinder.common.database.activityContext import com.shabinder.common.di.Dir import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.createDirectories @@ -38,6 +41,7 @@ import com.shabinder.common.root.SpotiFlyerRootContent import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks import com.shabinder.common.ui.SpotiFlyerTheme import com.shabinder.common.ui.colorOffWhite +import com.shabinder.common.ui.showPopUpMessage import com.shabinder.database.Database import com.tonyodev.fetch2.Status import kotlinx.coroutines.* @@ -46,7 +50,7 @@ import org.koin.android.ext.android.inject const val disableDozeCode = 1223 -class MainActivity : ComponentActivity() { +class MainActivity : ComponentActivity(), PaymentResultListener { private val database: Database by inject() private val fetcher: FetchPlatformQueryResult by inject() @@ -77,7 +81,6 @@ class MainActivity : ComponentActivity() { insets } } - root = SpotiFlyerRootContent(rootComponent(::spotiFlyerRoot),statusBarHeight) } } @@ -90,6 +93,7 @@ class MainActivity : ComponentActivity() { requestStoragePermission() disableDozeMode(disableDozeCode) dir.createDirectories() + Checkout.preload(applicationContext) } private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot = @@ -208,4 +212,24 @@ class MainActivity : ComponentActivity() { } } + override fun onPaymentError(errorCode: Int, response: String?) { + try{ + showPopUpMessage("Payment Failed, Response:$response") + }catch (e: Exception){ + Log.d("Razorpay Payment","Exception in onPaymentSuccess $response") + } + } + + override fun onPaymentSuccess(razorpayPaymentId: String?) { + try{ + showPopUpMessage("Payment Successful, ThankYou!") + }catch (e: Exception){ + showPopUpMessage("Razorpay Payment, Error Occurred.") + Log.d("Razorpay Payment","Exception in onPaymentSuccess, ${e.message}") + } + } + + init { + activityContext = this + } } diff --git a/buildSrc/buildSrc/src/main/kotlin/Versions.kt b/buildSrc/buildSrc/src/main/kotlin/Versions.kt index d53ddc96..4ac9b941 100644 --- a/buildSrc/buildSrc/src/main/kotlin/Versions.kt +++ b/buildSrc/buildSrc/src/main/kotlin/Versions.kt @@ -1,7 +1,7 @@ @file:Suppress("MayBeConstant", "SpellCheckingInspection") object Versions { - const val versionName = "2.2" + const val versionName = "2.2.0" const val kotlinVersion = "1.4.30" const val coroutinesVersion = "1.4.2" @@ -67,7 +67,7 @@ object JetBrains { object Compose { // __LATEST_COMPOSE_RELEASE_VERSION__ - const val VERSION = "0.3.0-build152" + const val VERSION = "0.3.0" const val gradlePlugin = "org.jetbrains.compose:compose-gradle-plugin:$VERSION" } } @@ -122,7 +122,7 @@ object Extras { const val mp3agic = "com.mpatric:mp3agic:0.9.1" const val kermit = "co.touchlab:kermit:${Versions.kermit}" object Android { - val razorpay = "com.razorpay:checkout:1.6.4" + val razorpay = "com.razorpay:checkout:1.6.5" val fetch = "androidx.tonyodev.fetch2:xfetch2:3.1.6" val appUpdator = "com.github.amitbd1508:AppUpdater:4.1.0" } diff --git a/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/AndroidImages.kt b/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/AndroidImages.kt index 1b4d8971..8c8f414c 100644 --- a/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/AndroidImages.kt +++ b/common/compose-ui/src/androidMain/kotlin/com/shabinder/common/ui/AndroidImages.kt @@ -2,9 +2,11 @@ package com.shabinder.common.ui +import androidx.annotation.DrawableRes import androidx.compose.foundation.Image import androidx.compose.runtime.Composable import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.vector.ImageVector import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.vectorResource import androidx.compose.ui.text.font.Font @@ -76,4 +78,7 @@ actual fun YoutubeLogo() = vectorResource(R.drawable.ic_youtube) actual fun YoutubeMusicLogo() = vectorResource(R.drawable.ic_youtube_music_logo) @Composable -actual fun GithubLogo() = vectorResource(R.drawable.ic_github) \ No newline at end of file +actual fun GithubLogo() = vectorResource(R.drawable.ic_github) + +@Composable +fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id) \ No newline at end of file diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt index e2d242f8..661b5228 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/list/SpotiFlyerListUi.kt @@ -42,7 +42,7 @@ fun SpotiFlyerListContent( } }else{ LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), content = { item { CoverImage(result.title, result.coverUrl, coroutineScope,component::loadImage) @@ -71,17 +71,14 @@ fun TrackCard( downloadTrack:()->Unit, loadImage:suspend (String)-> ImageBitmap? ) { - /*val status = remember { mutableStateOf(track.downloaded.name()) } - LaunchedEffect(track.downloaded.name()){ - status.value = track.downloaded.name() - }*/ Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(horizontal = 8.dp)) { ImageLoad( - {loadImage(track.albumArtURL)}, + track.albumArtURL, + loadImage, "Album Art", modifier = Modifier - .width(75.dp) - .height(90.dp) + .width(70.dp) + .height(70.dp) .clip(MaterialTheme.shapes.medium) ) Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { @@ -133,11 +130,13 @@ fun CoverImage( horizontalAlignment = Alignment.CenterHorizontally ) { ImageLoad( - { loadImage(coverURL) }, + coverURL, + loadImage, "Cover Image", modifier = Modifier - .width(210.dp) - .height(230.dp) + .padding(12.dp) + .width(190.dp) + .height(210.dp) .clip(MaterialTheme.shapes.medium) ) Text( diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt index a3447ba0..a055ec2c 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/main/SpotiFlyerMainUi.kt @@ -10,6 +10,7 @@ import androidx.compose.material.* import androidx.compose.material.Icon import androidx.compose.material.TabRowDefaults.tabIndicatorOffset import androidx.compose.material.Text +import androidx.compose.material.TextFieldDefaults.textFieldColors import androidx.compose.material.icons.Icons import androidx.compose.material.icons.outlined.History import androidx.compose.material.icons.outlined.Info @@ -17,6 +18,7 @@ import androidx.compose.material.icons.rounded.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.ImageBitmap @@ -130,10 +132,12 @@ fun SearchPanel( BorderStroke(2.dp, Brush.horizontalGradient(listOf(colorPrimary, colorAccent))), RoundedCornerShape(30.dp) ), - backgroundColor = Color.Black, shape = RoundedCornerShape(size = 30.dp), - activeColor = transparent, - inactiveColor = transparent, + colors = textFieldColors( + focusedIndicatorColor = Color.Transparent, + unfocusedIndicatorColor = Color.Transparent, + backgroundColor = Color.Black + ) ) OutlinedButton( modifier = Modifier.padding(12.dp).wrapContentWidth(), @@ -171,28 +175,28 @@ fun AboutColumn(modifier: Modifier = Modifier) { imageVector = SpotifyLogo(), "Open Spotify", tint = Color.Unspecified, - modifier = Modifier.clickable( + modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { openPlatform("com.spotify.music","http://open.spotify.com") }) ) Spacer(modifier = modifier.padding(start = 16.dp)) Icon(imageVector = GaanaLogo(), "Open Gaana", tint = Color.Unspecified, - modifier = Modifier.clickable( + modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { openPlatform("com.gaana","http://gaana.com") }) ) Spacer(modifier = modifier.padding(start = 16.dp)) Icon(imageVector = YoutubeLogo(), "Open Youtube", tint = Color.Unspecified, - modifier = Modifier.clickable( + modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { openPlatform("com.google.android.youtube","http://m.youtube.com") }) ) Spacer(modifier = modifier.padding(start = 12.dp)) Icon(imageVector = YoutubeMusicLogo(), "Open Youtube Music", tint = Color.Unspecified, - modifier = Modifier.clickable( + modifier = Modifier.clip(SpotiFlyerShapes.small).clickable( onClick = { openPlatform("com.google.android.apps.youtube.music","https://music.youtube.com/") }) ) } @@ -251,7 +255,7 @@ fun AboutColumn(modifier: Modifier = Modifier) { .clickable(onClick = { giveDonation() }), verticalAlignment = Alignment.CenterVertically ) { - Icon(Icons.Rounded.MailOutline,"Support Developer") + Icon(Icons.Rounded.CardGiftcard,"Support Developer") Spacer(modifier = Modifier.padding(start = 16.dp)) Column { Text( @@ -296,7 +300,7 @@ fun HistoryColumn( onItemClicked: (String) -> Unit ) { LazyColumn( - verticalArrangement = Arrangement.spacedBy(8.dp), + verticalArrangement = Arrangement.spacedBy(12.dp), content = { items(list.distinctBy { it.coverUrl }) { DownloadRecordItem( @@ -318,9 +322,10 @@ fun DownloadRecordItem( ) { Row(verticalAlignment = Alignment.CenterVertically,modifier = Modifier.fillMaxWidth().padding(end = 8.dp)) { ImageLoad( - { loadImage(item.coverUrl) }, + item.coverUrl, + loadImage, "Album Art", - modifier = Modifier.height(75.dp).width(90.dp) + modifier = Modifier.height(70.dp).width(70.dp).clip(SpotiFlyerShapes.medium) ) Column(modifier = Modifier.padding(horizontal = 8.dp).height(60.dp).weight(1f),verticalArrangement = Arrangement.SpaceEvenly) { Text(item.name,maxLines = 1,overflow = TextOverflow.Ellipsis,style = SpotiFlyerTypography.h6,color = colorAccent) diff --git a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/ExpectImages.kt b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/ExpectImages.kt index f0004f45..b2fbdc88 100644 --- a/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/ExpectImages.kt +++ b/common/compose-ui/src/commonMain/kotlin/com/shabinder/common/ui/ExpectImages.kt @@ -9,18 +9,19 @@ import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.vector.ImageVector +import androidx.compose.ui.layout.ContentScale import kotlinx.coroutines.withContext @Composable -fun ImageLoad(loader: suspend () -> ImageBitmap?, desc: String = "Album Art", modifier:Modifier = Modifier, placeholder:ImageVector = PlaceHolderImage()) { - var pic by remember { mutableStateOf(null) } - LaunchedEffect(loader){ +fun ImageLoad(link:String,loader:suspend (String) ->ImageBitmap?, desc: String = "Album Art", modifier:Modifier = Modifier, placeholder:ImageVector = PlaceHolderImage()) { + var pic by remember(link) { mutableStateOf(null) } + LaunchedEffect(link){ withContext(dispatcherIO) { - pic = loader() + pic = loader(link) } } Crossfade(pic){ - if(pic == null) Image(placeholder, desc, modifier) else Image(pic!!, desc, modifier) + if(it == null) Image(placeholder, desc, modifier,contentScale = ContentScale.Crop) else Image(it, desc, modifier,contentScale = ContentScale.Crop) } } diff --git a/common/compose-ui/src/main/res/drawable/ic_arrow.xml b/common/compose-ui/src/main/res/drawable/ic_arrow.xml index a426c154..0d138470 100644 --- a/common/compose-ui/src/main/res/drawable/ic_arrow.xml +++ b/common/compose-ui/src/main/res/drawable/ic_arrow.xml @@ -14,8 +14,8 @@ ~ along with this program. If not, see . --> - + diff --git a/common/compose-ui/src/main/res/drawable/ic_error.xml b/common/compose-ui/src/main/res/drawable/ic_error.xml index 9ebd8a0e..e2faa77e 100644 --- a/common/compose-ui/src/main/res/drawable/ic_error.xml +++ b/common/compose-ui/src/main/res/drawable/ic_error.xml @@ -15,7 +15,7 @@ --> diff --git a/common/compose-ui/src/main/res/drawable/ic_github.xml b/common/compose-ui/src/main/res/drawable/ic_github.xml index 0e14b28a..5eb8b1a0 100644 --- a/common/compose-ui/src/main/res/drawable/ic_github.xml +++ b/common/compose-ui/src/main/res/drawable/ic_github.xml @@ -14,8 +14,8 @@ ~ along with this program. If not, see . --> - + diff --git a/common/compose-ui/src/main/res/drawable/ic_tick.xml b/common/compose-ui/src/main/res/drawable/ic_tick.xml index 47903522..5b9e2e13 100644 --- a/common/compose-ui/src/main/res/drawable/ic_tick.xml +++ b/common/compose-ui/src/main/res/drawable/ic_tick.xml @@ -15,7 +15,7 @@ --> diff --git a/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt b/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt index 563e4f2d..45339fd1 100644 --- a/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt +++ b/common/database/src/androidMain/kotlin/com/shabinder/common/database/Actual.kt @@ -1,5 +1,6 @@ package com.shabinder.common.database +import android.annotation.SuppressLint import android.content.Context import co.touchlab.kermit.LogcatLogger import co.touchlab.kermit.Logger @@ -8,6 +9,12 @@ import com.squareup.sqldelight.android.AndroidSqliteDriver lateinit var appContext: Context +/* +* As MainActivity is God Activity , hence its active almost throughout App's lifetime +* */ +@SuppressLint("StaticFieldLeak") +lateinit var activityContext: Context + actual fun createDatabase(): Database { val driver = AndroidSqliteDriver(Database.Schema, appContext, "Database.db") return Database(driver) diff --git a/common/dependency-injection/build.gradle.kts b/common/dependency-injection/build.gradle.kts index 288a881f..f8c411d4 100644 --- a/common/dependency-injection/build.gradle.kts +++ b/common/dependency-injection/build.gradle.kts @@ -34,6 +34,7 @@ kotlin { implementation(Ktor.clientAndroid) implementation(Extras.Android.fetch) implementation(Koin.android) + implementation(Extras.Android.razorpay) //api(files("$rootDir/libs/mobile-ffmpeg.aar")) } } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt index 7331b49f..f98c5583 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/AndroidActual.kt @@ -1,5 +1,6 @@ package com.shabinder.common.di +import android.app.Activity import android.content.Intent import android.content.pm.PackageManager import android.net.Uri @@ -7,22 +8,25 @@ import androidx.core.content.ContextCompat import com.github.kiulian.downloader.model.YoutubeVideo import com.github.kiulian.downloader.model.formats.Format import com.github.kiulian.downloader.model.quality.AudioQuality -import com.shabinder.common.database.appContext +import com.razorpay.Checkout +import com.shabinder.common.database.activityContext import com.shabinder.common.di.worker.ForegroundService import com.shabinder.common.models.TrackDetails +import com.shabinder.common.ui.R +import org.json.JSONObject actual fun openPlatform(packageID:String, platformLink:String){ - val manager: PackageManager = appContext.packageManager + val manager: PackageManager = activityContext.packageManager try { val intent = manager.getLaunchIntentForPackage(packageID) ?: throw PackageManager.NameNotFoundException() intent.addCategory(Intent.CATEGORY_LAUNCHER) - appContext.startActivity(intent) + activityContext.startActivity(intent) } catch (e: PackageManager.NameNotFoundException) { val uri: Uri = Uri.parse(platformLink) val intent = Intent(Intent.ACTION_VIEW, uri) - appContext.startActivity(intent) + activityContext.startActivity(intent) } } @@ -34,18 +38,44 @@ actual fun shareApp(){ } val shareIntent = Intent.createChooser(sendIntent, null) - appContext.startActivity(shareIntent) + activityContext.startActivity(shareIntent) } -actual fun giveDonation(){ - //TODO -} +actual fun giveDonation() = startPayment() +private fun startPayment(mainActivity: Activity = activityContext as Activity) { + /* + * You need to pass current activity in order to let Razorpay create CheckoutActivity + * */ + val co = Checkout().apply { + setKeyID("rzp_live_3ZQeoFYOxjmXye") + setImage(R.drawable.ic_spotiflyer_logo) + } + + try { + val preFill = JSONObject() + + val options = JSONObject().apply { + put("name","SpotiFlyer") + put("description","Thanks For the Donation!") + //You can omit the image option to fetch the image from dashboard + //put("image","https://github.com/Shabinder/SpotiFlyer/raw/master/app/SpotifyDownload.png") + put("currency","INR") + put("amount","4900") + put("prefill",preFill) + } + + co.open(mainActivity,options) + }catch (e: Exception){ + //showPop("Error in payment: "+ e.message) + e.printStackTrace() + } +} actual fun queryActiveTracks() { - val serviceIntent = Intent(appContext, ForegroundService::class.java).apply { + val serviceIntent = Intent(activityContext, ForegroundService::class.java).apply { action = "query" } - ContextCompat.startForegroundService(appContext, serviceIntent) + ContextCompat.startForegroundService(activityContext, serviceIntent) } actual suspend fun downloadTracks( @@ -54,9 +84,9 @@ actual suspend fun downloadTracks( saveFileWithMetaData:suspend (mp3ByteArray:ByteArray, trackDetails: TrackDetails) -> Unit ){ if(!list.isNullOrEmpty()){ - val serviceIntent = Intent(appContext, ForegroundService::class.java) + val serviceIntent = Intent(activityContext, ForegroundService::class.java) serviceIntent.putParcelableArrayListExtra("object",ArrayList(list)) - appContext.let { ContextCompat.startForegroundService(it, serviceIntent) } + activityContext.let { ContextCompat.startForegroundService(it, serviceIntent) } } } diff --git a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt index 0b8e1238..42ae2051 100644 --- a/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt +++ b/common/dependency-injection/src/androidMain/kotlin/com/shabinder/common/di/worker/ForegroundService.kt @@ -32,6 +32,7 @@ import androidx.core.app.NotificationCompat import androidx.core.net.toUri import co.touchlab.kermit.Kermit import com.github.kiulian.downloader.YoutubeDownloader +import com.github.kiulian.downloader.model.formats.Format import com.shabinder.common.di.Dir import com.shabinder.common.di.FetchPlatformQueryResult import com.shabinder.common.di.getData @@ -178,37 +179,37 @@ class ForegroundService : Service(),CoroutineScope{ private fun downloadTrack(videoID:String, track: TrackDetails){ launch { try { - /*val audioData = ytDownloader.getVideo(videoID).getData() - - audioData?.let { - val url: String = it.url() - logger.d("DHelper Link Found") { url } - }*/ val url = fetcher.youtubeMp3.getMp3DownloadLink(videoID) if (url == null){ - sendTrackBroadcast(Status.FAILED.name,track) - allTracksStatus[track.title] = DownloadStatus.Failed - } else{ - val request= Request(url, track.outputFilePath).apply{ - priority = Priority.NORMAL - networkType = NetworkType.ALL - } - fetch.enqueue(request, - { request1 -> - requestMap[request1] = track - logger.d(tag){"Enqueuing Download"} - }, - { error -> - logger.d(tag){"Enqueuing Error:${error.throwable.toString()}"} - } - ) - } - }catch (e: java.lang.Exception){ + val audioData:Format = ytDownloader.getVideo(videoID).getData() ?: throw Exception("Java YT Dependency Error") + val ytUrl: String = audioData.url() + enqueueDownload(ytUrl,track) + } else enqueueDownload(url,track) + }catch (e: Exception){ logger.d("Service YT Error"){e.message.toString()} + sendTrackBroadcast(Status.FAILED.name,track) + allTracksStatus[track.title] = DownloadStatus.Failed } } } + + private fun enqueueDownload(url:String,track:TrackDetails){ + val request= Request(url, track.outputFilePath).apply{ + priority = Priority.NORMAL + networkType = NetworkType.ALL + } + fetch.enqueue(request, + { request1 -> + requestMap[request1] = track + logger.d(tag){"Enqueuing Download"} + }, + { error -> + logger.d(tag){"Enqueuing Error:${error.throwable.toString()}"} + } + ) + } + /** * Fetch Listener/ Responsible for Fetch Behaviour **/ diff --git a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt index 68155d7a..d631f47e 100644 --- a/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt +++ b/common/dependency-injection/src/commonMain/kotlin/com/shabinder/common/di/Dir.kt @@ -49,7 +49,7 @@ suspend fun downloadFile(url: String): Flow { } } fun getNameURL(url: String): String { - return url.substring(url.lastIndexOf('/') + 1, url.length) + return url.substring(url.lastIndexOf('/',url.lastIndexOf('/')-1) + 1, url.length).replace('/','_') } /* * Call this function at startup! diff --git a/common/dependency-injection/src/main/res/drawable/ic_spotiflyer_logo.xml b/common/dependency-injection/src/main/res/drawable/ic_spotiflyer_logo.xml new file mode 100644 index 00000000..bc68c3c7 --- /dev/null +++ b/common/dependency-injection/src/main/res/drawable/ic_spotiflyer_logo.xml @@ -0,0 +1,47 @@ + + + + + + + + + + + + + + + + + + + + + + + + +