Build:0.3.0,UI optimisations ,Anim fixes.

This commit is contained in:
shabinder 2021-02-25 03:28:02 +05:30
parent 1f548e82e0
commit ec1402ef2e
16 changed files with 196 additions and 76 deletions

View File

@ -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
}
}

View File

@ -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"
}

View File

@ -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)
actual fun GithubLogo() = vectorResource(R.drawable.ic_github)
@Composable
fun vectorResource(@DrawableRes id: Int) = ImageVector.Companion.vectorResource(id)

View File

@ -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(

View File

@ -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)

View File

@ -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<ImageBitmap?>(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<ImageBitmap?>(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)
}
}

View File

@ -14,8 +14,8 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="36dp"
android:height="32dp" android:viewportWidth="512" android:viewportHeight="512">
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="40dp"
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#516AEC" android:pathData="m296,288 l60,-60c7.73,-7.73 17.86,-11.6 28,-11.6 10.055,0 20.101,3.806 27.806,11.407 15.612,15.402 15.207,41.18 -0.3,56.687l-134.293,134.293c-11.716,11.716 -30.711,11.716 -42.426,0l-134.787,-134.787c-7.73,-7.73 -11.6,-17.86 -11.6,-28 0,-10.055 3.806,-20.101 11.407,-27.806 15.402,-15.612 41.18,-15.207 56.687,0.3l59.506,59.506v-232c0,-22.091 17.909,-40 40,-40 22.091,0 40,17.909 40,40z"/>
<path android:fillColor="#EC7EBA" android:pathData="m411.51,284.49 l-134.3,134.3c-11.71,11.71 -30.71,11.71 -42.42,0l-12.74,-12.74c10.69,4.06 23.23,1.77 31.84,-6.84l134.29,-134.29c12.51,-12.51 15.19,-31.7 7.57,-46.74 5.86,1.81 11.39,5.03 16.06,9.63 15.61,15.4 15.2,41.18 -0.3,56.68z"/>
<path android:fillColor="#EC7EBA" android:pathData="m251.88,27.72c-3.46,-3.46 -7.55,-6.29 -12.08,-8.3 4.95,-2.2 10.43,-3.42 16.2,-3.42 11.04,0 21.04,4.48 28.28,11.72s11.72,17.24 11.72,28.28v232l-15.329,15.329c-6.3,6.3 -17.071,1.838 -17.071,-7.071v-240.258c0,-11.04 -4.48,-21.04 -11.72,-28.28z"/>

View File

@ -15,7 +15,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="38dp" android:height="38dp"
android:width="40dp" android:height="40dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="m512,256c0,141.387 -114.613,256 -256,256s-256,-114.613 -256,-256 114.613,-256 256,-256 256,114.613 256,256zM512,256">
<aapt:attr name="android:fillColor">

View File

@ -14,8 +14,8 @@
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="38dp"
android:height="38dp" android:viewportWidth="512" android:viewportHeight="512">
<vector xmlns:android="http://schemas.android.com/apk/res/android" android:width="34dp"
android:height="34dp" android:viewportWidth="512" android:viewportHeight="512">
<path android:fillColor="#4EA4FF" android:pathData="M255.968,5.329C114.624,5.329 0,120.401 0,262.353c0,113.536 73.344,209.856 175.104,243.872c12.8,2.368 17.472,-5.568 17.472,-12.384c0,-6.112 -0.224,-22.272 -0.352,-43.712c-71.2,15.52 -86.24,-34.464 -86.24,-34.464c-11.616,-29.696 -28.416,-37.6 -28.416,-37.6c-23.264,-15.936 1.728,-15.616 1.728,-15.616c25.696,1.824 39.2,26.496 39.2,26.496c22.848,39.264 59.936,27.936 74.528,21.344c2.304,-16.608 8.928,-27.936 16.256,-34.368c-56.832,-6.496 -116.608,-28.544 -116.608,-127.008c0,-28.064 9.984,-51.008 26.368,-68.992c-2.656,-6.496 -11.424,-32.64 2.496,-68c0,0 21.504,-6.912 70.4,26.336c20.416,-5.696 42.304,-8.544 64.096,-8.64c21.728,0.128 43.648,2.944 64.096,8.672c48.864,-33.248 70.336,-26.336 70.336,-26.336c13.952,35.392 5.184,61.504 2.56,68c16.416,17.984 26.304,40.928 26.304,68.992c0,98.72 -59.84,120.448 -116.864,126.816c9.184,7.936 17.376,23.616 17.376,47.584c0,34.368 -0.32,62.08 -0.32,70.496c0,6.88 4.608,14.88 17.6,12.352C438.72,472.145 512,375.857 512,262.353C512,120.401 397.376,5.329 255.968,5.329z"/>
</vector>

View File

@ -15,7 +15,7 @@
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
android:width="36dp" android:height="36dp"
android:width="40dp" android:height="40dp"
android:viewportWidth="512" android:viewportHeight="512">
<path android:pathData="m512,256c0,141.387 -114.613,256 -256,256s-256,-114.613 -256,-256 114.613,-256 256,-256 256,114.613 256,256zM512,256">
<aapt:attr name="android:fillColor">

View File

@ -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)

View File

@ -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"))
}
}

View File

@ -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<TrackDetails>(list))
appContext.let { ContextCompat.startForegroundService(it, serviceIntent) }
activityContext.let { ContextCompat.startForegroundService(it, serviceIntent) }
}
}

View File

@ -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
**/

View File

@ -49,7 +49,7 @@ suspend fun downloadFile(url: String): Flow<DownloadResult> {
}
}
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!

View File

@ -0,0 +1,47 @@
<!--
~ Copyright (c) 2021 Shabinder Singh
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ along with this program. If not, see <https://www.gnu.org/licenses/>.
-->
<vector android:height="150dp" android:viewportHeight="512"
android:viewportWidth="512" android:width="150dp"
xmlns:aapt="http://schemas.android.com/aapt" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:pathData="M256,256m-256,0a256,256 0,1 1,512 0a256,256 0,1 1,-512 0">
<aapt:attr name="android:fillColor">
<gradient android:endX="437.019" android:endY="74.981"
android:startX="74.981" android:startY="437.019" android:type="linear">
<item android:color="#FF736BFD" android:offset="0"/>
<item android:color="#FFF54187" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:fillColor="#FF000000" android:pathData="M377,356.7c-68.9,-45.4 -155.6,-56.4 -257.6,-32.7c-20.5,4.8 -13.6,35.8 7.3,31.2C290.7,317 351.6,386 368.2,386C384,386 390.2,365.4 377,356.7z"/>
<path android:fillColor="#FF000000" android:pathData="M112.1,275.1C203.9,253.4 308.1,266 384,308c18.5,10.2 34,-17.8 15.5,-28c-82.7,-45.7 -195.6,-59.5 -294.7,-36C84.2,248.8 91.5,280 112.1,275.1L112.1,275.1z"/>
<path android:fillColor="#FF000000" android:pathData="M100,191.9c96.6,-29.6 232.2,-13.4 308.7,36.9c17.6,11.5 35.3,-15.1 17.6,-26.7c-84.9,-55.8 -229.2,-73.3 -335.6,-40.8C70.4,167.5 79.9,198.1 100,191.9L100,191.9z"/>
<path android:pathData="M507.8,438.2c-1.6,97.2 -141.9,97.1 -143.5,0C365.9,341 506.2,341 507.8,438.2z">
<aapt:attr name="android:fillColor">
<gradient android:endX="384.197" android:endY="490.009"
android:startX="487.832" android:startY="386.374" android:type="linear">
<item android:color="#FF736BFD" android:offset="0"/>
<item android:color="#FFF54187" android:offset="1"/>
</gradient>
</aapt:attr>
</path>
<path android:fillColor="#FF000000"
android:pathData="M486.8,456.8c-0.6,-2.4 -6.9,-1 -8.5,-1.4c11.5,-82 -82.4,-86.7 -87.1,-22.2c0.3,1.8 -1,6.7 2.2,6.6c0,0 8.6,0 8.6,0c3.1,0.1 2,-4.7 2.2,-6.6c0.1,-23.3 35,-23.3 35.2,0c0,0 0,6.9 0,6.9c-0.1,2.8 4.4,2.8 4.3,0c5,-35.2 -43.8,-40.1 -43.8,-4.7h-4.3c-1.6,-53.7 77.2,-55.9 78.4,-2.2c0,0 0,24.4 0,24.4c-0.1,2.9 3.8,2.1 5.6,2.2l-20.7,21l-20.7,-21c1.8,-0.1 5.6,0.7 5.6,-2.2c0,0 0,-8.8 0,-8.8c0,-2.8 -4.4,-2.8 -4.3,0c0,0 0,6.6 0,6.6c-2.2,0.2 -11.3,-1.3 -8,3.7c0,0 25.9,26.3 25.9,26.3c0.8,0.9 2.2,0.9 3.1,0C460.6,484.4 489.4,458.3 486.8,456.8z"
android:strokeColor="#000" android:strokeWidth=".75"/>
<path android:fillColor="#00000000"
android:pathData="M510,437.5c-1.7,96.2 -142.1,96.2 -143.8,0C367.9,341.3 508.4,341.3 510,437.5z"
android:strokeColor="#000" android:strokeWidth="6"/>
</vector>