2021-03-18 19:11:45 +01:00
|
|
|
/*
|
|
|
|
* * 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/>.
|
|
|
|
*/
|
|
|
|
|
2021-02-25 14:28:33 +01:00
|
|
|
package com.shabinder.spotiflyer
|
2021-01-26 13:54:28 +01:00
|
|
|
|
2021-02-10 18:25:36 +01:00
|
|
|
import android.annotation.SuppressLint
|
2021-02-22 18:38:33 +01:00
|
|
|
import android.content.BroadcastReceiver
|
2021-02-10 18:25:36 +01:00
|
|
|
import android.content.Context
|
|
|
|
import android.content.Intent
|
2021-02-22 18:38:33 +01:00
|
|
|
import android.content.IntentFilter
|
2021-02-10 18:25:36 +01:00
|
|
|
import android.os.Build
|
2021-01-26 13:54:28 +01:00
|
|
|
import android.os.Bundle
|
2021-02-10 18:25:36 +01:00
|
|
|
import android.os.PowerManager
|
2021-02-22 18:38:33 +01:00
|
|
|
import android.util.Log
|
2021-02-10 18:25:36 +01:00
|
|
|
import androidx.activity.ComponentActivity
|
2021-02-11 21:51:11 +01:00
|
|
|
import androidx.activity.compose.setContent
|
2021-02-26 11:29:10 +01:00
|
|
|
import androidx.compose.animation.*
|
2021-02-25 16:23:10 +01:00
|
|
|
import androidx.compose.foundation.background
|
|
|
|
import androidx.compose.foundation.layout.*
|
|
|
|
import androidx.compose.material.*
|
|
|
|
import androidx.compose.material.icons.Icons
|
|
|
|
import androidx.compose.material.icons.rounded.SdStorage
|
|
|
|
import androidx.compose.material.icons.rounded.SystemSecurityUpdate
|
2021-02-25 14:28:33 +01:00
|
|
|
import androidx.compose.runtime.*
|
2021-02-25 16:23:10 +01:00
|
|
|
import androidx.compose.ui.Alignment
|
|
|
|
import androidx.compose.ui.Modifier
|
|
|
|
import androidx.compose.ui.graphics.Color
|
2021-02-24 18:02:01 +01:00
|
|
|
import androidx.compose.ui.platform.LocalView
|
2021-02-25 16:23:10 +01:00
|
|
|
import androidx.compose.ui.text.font.FontWeight
|
|
|
|
import androidx.compose.ui.text.style.TextAlign
|
2021-02-24 18:02:01 +01:00
|
|
|
import androidx.compose.ui.unit.dp
|
2021-02-25 16:23:10 +01:00
|
|
|
import androidx.compose.ui.unit.sp
|
2021-02-24 18:02:01 +01:00
|
|
|
import androidx.core.view.WindowCompat
|
2021-02-22 18:38:33 +01:00
|
|
|
import androidx.lifecycle.lifecycleScope
|
2021-02-07 17:19:48 +01:00
|
|
|
import com.arkivanov.decompose.ComponentContext
|
2021-02-25 14:28:33 +01:00
|
|
|
import com.arkivanov.decompose.extensions.compose.jetbrains.rememberRootComponent
|
2021-02-07 17:19:48 +01:00
|
|
|
import com.arkivanov.mvikotlin.logging.store.LoggingStoreFactory
|
|
|
|
import com.arkivanov.mvikotlin.main.store.DefaultStoreFactory
|
2021-02-24 22:58:02 +01:00
|
|
|
import com.razorpay.Checkout
|
|
|
|
import com.razorpay.PaymentResultListener
|
|
|
|
import com.shabinder.common.database.activityContext
|
2021-02-25 16:23:10 +01:00
|
|
|
import com.shabinder.common.di.*
|
2021-02-21 15:21:43 +01:00
|
|
|
import com.shabinder.common.models.DownloadStatus
|
2021-02-22 18:38:33 +01:00
|
|
|
import com.shabinder.common.models.TrackDetails
|
2021-02-07 17:19:48 +01:00
|
|
|
import com.shabinder.common.root.SpotiFlyerRoot
|
2021-02-10 18:25:36 +01:00
|
|
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
2021-02-25 16:23:10 +01:00
|
|
|
import com.shabinder.common.uikit.*
|
2021-02-26 00:59:54 +01:00
|
|
|
import com.shabinder.spotiflyer.utils.*
|
2021-02-22 18:38:33 +01:00
|
|
|
import com.tonyodev.fetch2.Status
|
2021-02-10 18:25:36 +01:00
|
|
|
import kotlinx.coroutines.*
|
2021-02-22 22:42:58 +01:00
|
|
|
import kotlinx.coroutines.flow.*
|
2021-02-07 17:19:48 +01:00
|
|
|
import org.koin.android.ext.android.inject
|
2021-03-07 20:58:47 +01:00
|
|
|
import com.shabinder.common.uikit.showPopUpMessage as uikitShowPopUpMessage
|
2021-01-26 13:54:28 +01:00
|
|
|
|
2021-02-10 18:25:36 +01:00
|
|
|
const val disableDozeCode = 1223
|
2021-02-07 17:19:48 +01:00
|
|
|
|
2021-02-26 10:41:25 +01:00
|
|
|
@ExperimentalAnimationApi
|
2021-02-24 22:58:02 +01:00
|
|
|
class MainActivity : ComponentActivity(), PaymentResultListener {
|
2021-02-10 18:25:36 +01:00
|
|
|
|
|
|
|
private val fetcher: FetchPlatformQueryResult by inject()
|
|
|
|
private val dir: Dir by inject()
|
|
|
|
private lateinit var root: SpotiFlyerRoot
|
|
|
|
private val callBacks: SpotiFlyerRootCallBacks
|
|
|
|
get() = root.callBacks
|
2021-02-22 22:42:58 +01:00
|
|
|
private val trackStatusFlow = MutableSharedFlow<HashMap<String, DownloadStatus>>(1)
|
2021-02-25 16:23:10 +01:00
|
|
|
private var permissionGranted = mutableStateOf(true)
|
2021-02-22 18:38:33 +01:00
|
|
|
private lateinit var updateUIReceiver: BroadcastReceiver
|
|
|
|
private lateinit var queryReceiver: BroadcastReceiver
|
2021-02-07 17:19:48 +01:00
|
|
|
|
2021-01-26 13:54:28 +01:00
|
|
|
override fun onCreate(savedInstanceState: Bundle?) {
|
|
|
|
super.onCreate(savedInstanceState)
|
2021-02-24 18:02:01 +01:00
|
|
|
// This app draws behind the system bars, so we want to handle fitting system windows
|
|
|
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
|
|
|
|
2021-01-26 13:54:28 +01:00
|
|
|
setContent {
|
2021-02-07 17:19:48 +01:00
|
|
|
SpotiFlyerTheme {
|
2021-02-12 00:17:46 +01:00
|
|
|
Surface(contentColor = colorOffWhite) {
|
2021-02-24 18:02:01 +01:00
|
|
|
|
|
|
|
var statusBarHeight by remember { mutableStateOf(27.dp) }
|
2021-02-25 16:23:10 +01:00
|
|
|
permissionGranted = remember { mutableStateOf(true) }
|
2021-02-24 18:02:01 +01:00
|
|
|
val view = LocalView.current
|
|
|
|
|
|
|
|
LaunchedEffect(view){
|
2021-02-26 10:41:25 +01:00
|
|
|
permissionGranted.value = checkPermissions()
|
2021-02-24 18:02:01 +01:00
|
|
|
view.setOnApplyWindowInsetsListener { _, insets ->
|
|
|
|
statusBarHeight = insets.systemWindowInsetTop.dp
|
|
|
|
insets
|
|
|
|
}
|
|
|
|
}
|
2021-02-25 16:23:10 +01:00
|
|
|
|
2021-02-26 10:41:25 +01:00
|
|
|
root = SpotiFlyerRootContent(rememberRootComponent(::spotiFlyerRoot),statusBarHeight)
|
|
|
|
|
2021-02-25 16:23:10 +01:00
|
|
|
|
2021-02-26 00:59:54 +01:00
|
|
|
NetworkDialog()
|
2021-02-26 10:41:25 +01:00
|
|
|
PermissionDialog()
|
2021-02-12 00:17:46 +01:00
|
|
|
}
|
2021-02-07 17:19:48 +01:00
|
|
|
}
|
2021-01-26 13:54:28 +01:00
|
|
|
}
|
2021-02-10 18:25:36 +01:00
|
|
|
initialise()
|
2021-01-26 13:54:28 +01:00
|
|
|
}
|
2021-02-07 17:19:48 +01:00
|
|
|
|
2021-02-10 18:25:36 +01:00
|
|
|
private fun initialise() {
|
|
|
|
checkIfLatestVersion()
|
|
|
|
dir.createDirectories()
|
2021-02-24 22:58:02 +01:00
|
|
|
Checkout.preload(applicationContext)
|
2021-02-26 11:29:10 +01:00
|
|
|
handleIntentFromExternalActivity()
|
2021-02-10 18:25:36 +01:00
|
|
|
}
|
2021-02-07 17:19:48 +01:00
|
|
|
|
2021-02-26 10:41:25 +01:00
|
|
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
|
|
|
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
|
|
|
permissionGranted.value = checkPermissions()
|
|
|
|
}
|
|
|
|
|
2021-02-07 17:19:48 +01:00
|
|
|
private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
|
|
|
|
SpotiFlyerRoot(
|
|
|
|
componentContext,
|
|
|
|
dependencies = object : SpotiFlyerRoot.Dependencies{
|
|
|
|
override val storeFactory = LoggingStoreFactory(DefaultStoreFactory)
|
2021-03-08 17:41:08 +01:00
|
|
|
override val database = this@MainActivity.dir.db
|
2021-02-07 17:19:48 +01:00
|
|
|
override val fetchPlatformQueryResult = this@MainActivity.fetcher
|
|
|
|
override val directories: Dir = this@MainActivity.dir
|
2021-03-07 20:58:47 +01:00
|
|
|
override val showPopUpMessage: (String) -> Unit = ::uikitShowPopUpMessage
|
2021-02-22 22:42:58 +01:00
|
|
|
override val downloadProgressReport: MutableSharedFlow<HashMap<String, DownloadStatus>> = trackStatusFlow
|
2021-02-07 17:19:48 +01:00
|
|
|
}
|
|
|
|
)
|
2021-02-10 18:25:36 +01:00
|
|
|
|
|
|
|
|
|
|
|
@SuppressLint("ObsoleteSdkInt")
|
|
|
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
|
|
|
super.onActivityResult(requestCode, resultCode, data)
|
|
|
|
if (requestCode == disableDozeCode) {
|
|
|
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
|
|
|
val pm =
|
|
|
|
getSystemService(Context.POWER_SERVICE) as PowerManager
|
|
|
|
val isIgnoringBatteryOptimizations =
|
|
|
|
pm.isIgnoringBatteryOptimizations(packageName)
|
|
|
|
if (isIgnoringBatteryOptimizations) {
|
|
|
|
// Ignoring battery optimization
|
2021-02-25 16:23:10 +01:00
|
|
|
permissionGranted.value = true
|
2021-02-10 18:25:36 +01:00
|
|
|
} else {
|
|
|
|
disableDozeMode(disableDozeCode)//Again Ask For Permission!!
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-22 18:38:33 +01:00
|
|
|
private fun initializeBroadcast(){
|
|
|
|
val intentFilter = IntentFilter().apply {
|
|
|
|
addAction(Status.QUEUED.name)
|
|
|
|
addAction(Status.FAILED.name)
|
|
|
|
addAction(Status.DOWNLOADING.name)
|
2021-02-22 22:42:58 +01:00
|
|
|
addAction(Status.COMPLETED.name)
|
2021-02-22 18:38:33 +01:00
|
|
|
addAction("Progress")
|
|
|
|
addAction("Converting")
|
|
|
|
}
|
|
|
|
updateUIReceiver = object : BroadcastReceiver() {
|
|
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
|
|
//Update Flow with latest details
|
|
|
|
if (intent != null) {
|
|
|
|
val trackDetails = intent.getParcelableExtra<TrackDetails?>("track")
|
|
|
|
trackDetails?.let { track ->
|
|
|
|
lifecycleScope.launch {
|
2021-02-22 22:42:58 +01:00
|
|
|
val latestMap = trackStatusFlow.replayCache.getOrElse(0
|
|
|
|
) { hashMapOf() }.apply {
|
2021-02-22 18:38:33 +01:00
|
|
|
this[track.title] = when (intent.action) {
|
|
|
|
Status.QUEUED.name -> DownloadStatus.Queued
|
|
|
|
Status.FAILED.name -> DownloadStatus.Failed
|
|
|
|
Status.DOWNLOADING.name -> DownloadStatus.Downloading()
|
|
|
|
"Progress" -> DownloadStatus.Downloading(intent.getIntExtra("progress", 0))
|
|
|
|
"Converting" -> DownloadStatus.Converting
|
2021-02-22 22:42:58 +01:00
|
|
|
Status.COMPLETED.name -> DownloadStatus.Downloaded
|
2021-02-22 18:38:33 +01:00
|
|
|
else -> DownloadStatus.NotDownloaded
|
|
|
|
}
|
|
|
|
}
|
2021-02-22 22:42:58 +01:00
|
|
|
trackStatusFlow.emit(latestMap)
|
|
|
|
Log.i("Track Update",track.title + track.downloaded.toString())
|
2021-02-22 18:38:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
val queryFilter = IntentFilter().apply { addAction("query_result") }
|
|
|
|
queryReceiver = object : BroadcastReceiver() {
|
|
|
|
override fun onReceive(context: Context?, intent: Intent?) {
|
|
|
|
//UI update here
|
|
|
|
if (intent != null){
|
|
|
|
@Suppress("UNCHECKED_CAST")
|
|
|
|
val trackList = intent.getSerializableExtra("tracks") as? HashMap<String, DownloadStatus>?
|
|
|
|
trackList?.let { list ->
|
|
|
|
Log.i("Service Response", "${list.size} Tracks Active")
|
|
|
|
lifecycleScope.launch {
|
2021-02-22 22:42:58 +01:00
|
|
|
trackStatusFlow.emit(list)
|
2021-02-22 18:38:33 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
registerReceiver(updateUIReceiver, intentFilter)
|
|
|
|
registerReceiver(queryReceiver, queryFilter)
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onResume() {
|
|
|
|
super.onResume()
|
|
|
|
initializeBroadcast()
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPause() {
|
|
|
|
super.onPause()
|
|
|
|
unregisterReceiver(updateUIReceiver)
|
|
|
|
unregisterReceiver(queryReceiver)
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2021-02-10 18:25:36 +01:00
|
|
|
override fun onNewIntent(intent: Intent?) {
|
|
|
|
super.onNewIntent(intent)
|
|
|
|
handleIntentFromExternalActivity(intent)
|
|
|
|
}
|
|
|
|
|
|
|
|
private fun handleIntentFromExternalActivity(intent: Intent? = getIntent()) {
|
|
|
|
if (intent?.action == Intent.ACTION_SEND) {
|
|
|
|
if ("text/plain" == intent.type) {
|
|
|
|
intent.getStringExtra(Intent.EXTRA_TEXT)?.let {
|
|
|
|
val filterLinkRegex = """http.+\w""".toRegex()
|
|
|
|
val string = it.replace("\n".toRegex(), " ")
|
|
|
|
val link = filterLinkRegex.find(string)?.value.toString()
|
2021-02-26 11:29:10 +01:00
|
|
|
Log.i("Intent",link)
|
|
|
|
lifecycleScope.launch {
|
|
|
|
while(!this@MainActivity::root.isInitialized){
|
|
|
|
delay(100)
|
|
|
|
}
|
|
|
|
if(isInternetAvailable)callBacks.searchLink(link)
|
|
|
|
}
|
2021-02-10 18:25:36 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-24 22:58:02 +01:00
|
|
|
override fun onPaymentError(errorCode: Int, response: String?) {
|
|
|
|
try{
|
2021-03-07 20:58:47 +01:00
|
|
|
uikitShowPopUpMessage("Payment Failed, Response:$response")
|
2021-02-24 22:58:02 +01:00
|
|
|
}catch (e: Exception){
|
|
|
|
Log.d("Razorpay Payment","Exception in onPaymentSuccess $response")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
override fun onPaymentSuccess(razorpayPaymentId: String?) {
|
|
|
|
try{
|
2021-03-07 20:58:47 +01:00
|
|
|
uikitShowPopUpMessage("Payment Successful, ThankYou!")
|
2021-02-24 22:58:02 +01:00
|
|
|
}catch (e: Exception){
|
2021-03-07 20:58:47 +01:00
|
|
|
uikitShowPopUpMessage("Razorpay Payment, Error Occurred.")
|
2021-02-24 22:58:02 +01:00
|
|
|
Log.d("Razorpay Payment","Exception in onPaymentSuccess, ${e.message}")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-02-25 16:23:10 +01:00
|
|
|
@Composable
|
2021-02-26 10:41:25 +01:00
|
|
|
private fun PermissionDialog(){
|
|
|
|
var askForPermission by remember { mutableStateOf(false) }
|
|
|
|
LaunchedEffect(Unit){
|
|
|
|
delay(2000)
|
|
|
|
askForPermission = true
|
|
|
|
}
|
2021-02-26 11:29:10 +01:00
|
|
|
AnimatedVisibility(
|
|
|
|
askForPermission && !permissionGranted.value
|
|
|
|
){
|
2021-02-26 10:41:25 +01:00
|
|
|
AlertDialog(
|
|
|
|
onDismissRequest = {},
|
|
|
|
buttons = {
|
|
|
|
TextButton({
|
|
|
|
requestStoragePermission()
|
|
|
|
disableDozeMode(disableDozeCode)
|
|
|
|
},Modifier.padding(bottom = 16.dp,start = 16.dp,end = 16.dp).fillMaxWidth().background(colorPrimary,shape = SpotiFlyerShapes.medium).padding(horizontal = 8.dp),
|
|
|
|
){
|
|
|
|
Text("Grant Permissions",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
|
2021-02-25 16:23:10 +01:00
|
|
|
}
|
2021-02-26 10:41:25 +01:00
|
|
|
},title = {Text("Required Permissions:",style = SpotiFlyerTypography.h5,textAlign = TextAlign.Center)},
|
|
|
|
backgroundColor = Color.DarkGray,
|
|
|
|
text = {
|
|
|
|
Column{
|
|
|
|
Spacer(modifier = Modifier.padding(8.dp))
|
|
|
|
Row(verticalAlignment = Alignment.CenterVertically,
|
|
|
|
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
|
|
|
|
) {
|
|
|
|
Icon(Icons.Rounded.SdStorage,"Storage Permission.")
|
|
|
|
Spacer(modifier = Modifier.padding(start = 16.dp))
|
|
|
|
Column {
|
|
|
|
Text(
|
|
|
|
text = "Storage Permission.",
|
|
|
|
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
|
|
|
)
|
|
|
|
Text(
|
|
|
|
text = "To download your favourite songs to this device.",
|
|
|
|
style = SpotiFlyerTypography.subtitle2,
|
|
|
|
)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
Row(
|
|
|
|
modifier = Modifier.fillMaxWidth().padding(top = 6.dp),
|
|
|
|
verticalAlignment = Alignment.CenterVertically
|
|
|
|
) {
|
|
|
|
Icon(Icons.Rounded.SystemSecurityUpdate,"Allow Background Running")
|
|
|
|
Spacer(modifier = Modifier.padding(start = 16.dp))
|
|
|
|
Column {
|
|
|
|
Text(
|
|
|
|
text = "Background Running.",
|
|
|
|
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold)
|
|
|
|
)
|
|
|
|
Text(
|
|
|
|
text = "To download all songs in background without any System Interruptions",
|
|
|
|
style = SpotiFlyerTypography.subtitle2,
|
|
|
|
)
|
|
|
|
}
|
2021-02-25 16:23:10 +01:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-02-26 10:41:25 +01:00
|
|
|
)
|
|
|
|
}
|
2021-02-25 16:23:10 +01:00
|
|
|
}
|
|
|
|
|
2021-02-24 22:58:02 +01:00
|
|
|
init {
|
|
|
|
activityContext = this
|
|
|
|
}
|
2021-02-07 17:19:48 +01:00
|
|
|
}
|