Network/Permissions Dialogs Anim.

This commit is contained in:
shabinder 2021-02-26 15:11:25 +05:30
parent bf6463a3e1
commit 65427793fc
11 changed files with 158 additions and 141 deletions

2
.gitignore vendored
View File

@ -117,3 +117,5 @@
/common/list/build/ /common/list/build/
/common/main/build/ /common/main/build/
/common/root/build/ /common/root/build/
/android/release/
/android/google-services.json

View File

@ -13,6 +13,8 @@ import android.os.PowerManager
import android.util.Log import android.util.Log
import androidx.activity.ComponentActivity import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.* import androidx.compose.material.*
@ -53,6 +55,7 @@ import org.koin.android.ext.android.inject
const val disableDozeCode = 1223 const val disableDozeCode = 1223
@ExperimentalAnimationApi
class MainActivity : ComponentActivity(), PaymentResultListener { class MainActivity : ComponentActivity(), PaymentResultListener {
private val database: Database by inject() private val database: Database by inject()
@ -76,30 +79,22 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
Surface(contentColor = colorOffWhite) { Surface(contentColor = colorOffWhite) {
var statusBarHeight by remember { mutableStateOf(27.dp) } var statusBarHeight by remember { mutableStateOf(27.dp) }
var askForPermission by remember { mutableStateOf(false) }
permissionGranted = remember { mutableStateOf(true) } permissionGranted = remember { mutableStateOf(true) }
val view = LocalView.current val view = LocalView.current
LaunchedEffect(view){ LaunchedEffect(view){
permissionGranted.value = (ContextCompat permissionGranted.value = checkPermissions()
.checkSelfPermission(this@MainActivity,
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&&
ContextCompat.checkSelfPermission(this@MainActivity,
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_GRANTED)
view.setOnApplyWindowInsetsListener { _, insets -> view.setOnApplyWindowInsetsListener { _, insets ->
statusBarHeight = insets.systemWindowInsetTop.dp statusBarHeight = insets.systemWindowInsetTop.dp
insets insets
} }
delay(2000)
askForPermission = true
} }
if(askForPermission && !permissionGranted.value) permissionDialog() root = SpotiFlyerRootContent(rememberRootComponent(::spotiFlyerRoot),statusBarHeight)
NetworkDialog() NetworkDialog()
root = SpotiFlyerRootContent(rememberRootComponent(::spotiFlyerRoot),statusBarHeight) PermissionDialog()
} }
} }
} }
@ -112,6 +107,11 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
Checkout.preload(applicationContext) Checkout.preload(applicationContext)
} }
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
permissionGranted.value = checkPermissions()
}
private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot = private fun spotiFlyerRoot(componentContext: ComponentContext): SpotiFlyerRoot =
SpotiFlyerRoot( SpotiFlyerRoot(
componentContext, componentContext,
@ -247,64 +247,65 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
} }
@Composable @Composable
private fun permissionDialog(){ private fun PermissionDialog(){
AlertDialog( var askForPermission by remember { mutableStateOf(false) }
onDismissRequest = {}, LaunchedEffect(Unit){
buttons = { delay(2000)
TextButton({ askForPermission = true
requestStoragePermission() }
disableDozeMode(disableDozeCode) AnimatedVisibility(askForPermission && !permissionGranted.value){
permissionGranted.value = (ContextCompat AlertDialog(
.checkSelfPermission(this@MainActivity, onDismissRequest = {},
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED buttons = {
&& TextButton({
ContextCompat.checkSelfPermission(this@MainActivity,Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_GRANTED) requestStoragePermission()
},Modifier.padding(bottom = 16.dp,start = 16.dp,end = 16.dp).fillMaxWidth().background(colorPrimary,shape = SpotiFlyerShapes.medium).padding(horizontal = 8.dp), 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) ){
} Text("Grant Permissions",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
},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( },title = {Text("Required Permissions:",style = SpotiFlyerTypography.h5,textAlign = TextAlign.Center)},
modifier = Modifier.fillMaxWidth().padding(top = 6.dp), backgroundColor = Color.DarkGray,
verticalAlignment = Alignment.CenterVertically text = {
) { Column{
Icon(Icons.Rounded.SystemSecurityUpdate,"Allow Background Running") Spacer(modifier = Modifier.padding(8.dp))
Spacer(modifier = Modifier.padding(start = 16.dp)) Row(verticalAlignment = Alignment.CenterVertically,
Column { modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
Text( ) {
text = "Background Running.", Icon(Icons.Rounded.SdStorage,"Storage Permission.")
style = SpotiFlyerTypography.h6.copy(fontWeight = FontWeight.SemiBold) Spacer(modifier = Modifier.padding(start = 16.dp))
) Column {
Text( Text(
text = "To download all songs in background without any System Interruptions", text = "Storage Permission.",
style = SpotiFlyerTypography.subtitle2, 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,
)
}
} }
} }
} }
} )
}
)
} }
init { init {

View File

@ -1,6 +1,7 @@
package com.shabinder.spotiflyer.utils package com.shabinder.spotiflyer.utils
import androidx.compose.animation.Crossfade import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.* import androidx.compose.foundation.layout.*
import androidx.compose.material.AlertDialog import androidx.compose.material.AlertDialog
@ -14,13 +15,13 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import com.shabinder.common.di.isInternetAvailable
import com.shabinder.common.di.isInternetAvailableState import com.shabinder.common.di.isInternetAvailableState
import com.shabinder.common.uikit.SpotiFlyerShapes import com.shabinder.common.uikit.SpotiFlyerShapes
import com.shabinder.common.uikit.SpotiFlyerTypography import com.shabinder.common.uikit.SpotiFlyerTypography
import com.shabinder.common.uikit.colorOffWhite import com.shabinder.common.uikit.colorOffWhite
import kotlinx.coroutines.delay import kotlinx.coroutines.delay
@ExperimentalAnimationApi
@Composable @Composable
fun NetworkDialog( fun NetworkDialog(
networkAvailability: State<Boolean?> = isInternetAvailableState() networkAvailability: State<Boolean?> = isInternetAvailableState()
@ -31,43 +32,41 @@ fun NetworkDialog(
delay(2600) delay(2600)
visible = true visible = true
} }
if(networkAvailability.value == false){
Crossfade(visible){ AnimatedVisibility(networkAvailability.value == false && visible){
if(it){ AlertDialog(
AlertDialog( onDismissRequest = {},
onDismissRequest = {}, buttons = {
buttons = { /* TextButton({
/* TextButton({ //Retry Network Connection
//Retry Network Connection },
}, Modifier.padding(bottom = 16.dp,start = 16.dp,end = 16.dp).fillMaxWidth().background(Color(0xFFFC5C7D),shape = RoundedCornerShape(size = 8.dp)).padding(horizontal = 8.dp),
Modifier.padding(bottom = 16.dp,start = 16.dp,end = 16.dp).fillMaxWidth().background(Color(0xFFFC5C7D),shape = RoundedCornerShape(size = 8.dp)).padding(horizontal = 8.dp), ){
){ Text("Retry",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
Text("Retry",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center) Icon(Icons.Rounded.SyncProblem,"Check Network Connection Again")
Icon(Icons.Rounded.SyncProblem,"Check Network Connection Again")
}
*/},
title = { Text("No Internet Connection!",
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center) },
backgroundColor = Color.DarkGray,
text = {
Column(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){
Spacer(modifier = Modifier.padding(8.dp))
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
) {
Image(Icons.Rounded.CloudOff,"No Internet.",Modifier.size(42.dp),colorFilter = ColorFilter.tint(
colorOffWhite))
Spacer(modifier = Modifier.padding(start = 16.dp))
Text(
text = "Please Check Your Network Connection.",
style = SpotiFlyerTypography.subtitle1
)
}
}
} }
,shape = SpotiFlyerShapes.medium) */},
title = { Text("No Internet Connection!",
style = SpotiFlyerTypography.h5,
textAlign = TextAlign.Center) },
backgroundColor = Color.DarkGray,
text = {
Column(horizontalAlignment = Alignment.CenterHorizontally,verticalArrangement = Arrangement.Center){
Spacer(modifier = Modifier.padding(8.dp))
Row(verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth().padding(vertical = 6.dp)
) {
Image(Icons.Rounded.CloudOff,"No Internet.",Modifier.size(42.dp),colorFilter = ColorFilter.tint(
colorOffWhite))
Spacer(modifier = Modifier.padding(start = 16.dp))
Text(
text = "Please Check Your Network Connection.",
style = SpotiFlyerTypography.subtitle1
)
}
}
} }
} ,shape = SpotiFlyerShapes.medium
)
} }
} }

View File

@ -5,10 +5,12 @@ import android.annotation.SuppressLint
import android.app.Activity import android.app.Activity
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.provider.Settings import android.provider.Settings
import androidx.core.content.ContextCompat
import com.github.javiersantos.appupdater.AppUpdater import com.github.javiersantos.appupdater.AppUpdater
import com.github.javiersantos.appupdater.enums.Display import com.github.javiersantos.appupdater.enums.Display
import com.github.javiersantos.appupdater.enums.UpdateFrom import com.github.javiersantos.appupdater.enums.UpdateFrom
@ -23,6 +25,17 @@ fun Activity.checkIfLatestVersion() {
start() start()
} }
} }
fun Activity.checkPermissions():Boolean{
return (ContextCompat
.checkSelfPermission(this,
Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED
&&
ContextCompat.checkSelfPermission(this,
Manifest.permission.REQUEST_IGNORE_BATTERY_OPTIMIZATIONS) == PackageManager.PERMISSION_GRANTED)
}
@SuppressLint("BatteryLife", "ObsoleteSdkInt") @SuppressLint("BatteryLife", "ObsoleteSdkInt")
fun Activity.disableDozeMode(requestCode:Int) { fun Activity.disableDozeMode(requestCode:Int) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {

View File

@ -4,19 +4,13 @@ import android.app.Activity
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.Uri import android.net.Uri
import androidx.compose.material.MaterialTheme
import androidx.compose.material.contentColorFor
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.MutableState import androidx.compose.runtime.MutableState
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.Shape
import androidx.core.content.ContextCompat import androidx.core.content.ContextCompat
import com.github.kiulian.downloader.model.YoutubeVideo import com.github.kiulian.downloader.model.YoutubeVideo
import com.github.kiulian.downloader.model.formats.Format import com.github.kiulian.downloader.model.formats.Format
import com.github.kiulian.downloader.model.quality.AudioQuality import com.github.kiulian.downloader.model.quality.AudioQuality
import com.razorpay.Checkout import com.razorpay.Checkout
import com.shabinder.common.database.R
import com.shabinder.common.database.activityContext import com.shabinder.common.database.activityContext
import com.shabinder.common.database.appContext import com.shabinder.common.database.appContext
import com.shabinder.common.di.worker.ForegroundService import com.shabinder.common.di.worker.ForegroundService
@ -49,6 +43,9 @@ actual fun Toast(
//We Have Android's Implementation of Toast so its just Empty //We Have Android's Implementation of Toast so its just Empty
} }
actual val isInternetAvailable:Boolean
get() = internetAvailability.value ?: true
actual fun showPopUpMessage(text: String){ actual fun showPopUpMessage(text: String){
android.widget.Toast.makeText(appContext,text, android.widget.Toast.LENGTH_SHORT).show() android.widget.Toast.makeText(appContext,text, android.widget.Toast.LENGTH_SHORT).show()
} }

View File

@ -22,11 +22,11 @@ import javax.net.SocketFactory
const val TAG = "C-Manager" const val TAG = "C-Manager"
val isInternetAvailable by lazy { ConnectionLiveData(appContext) } val internetAvailability by lazy { ConnectionLiveData(appContext) }
@Composable @Composable
fun isInternetAvailableState(): State<Boolean?>{ fun isInternetAvailableState(): State<Boolean?>{
return isInternetAvailable.observeAsState() return internetAvailability.observeAsState()
} }
/** /**

View File

@ -44,20 +44,6 @@ val kotlinxSerializer = KotlinxSerializer( Json {
isLenient = true isLenient = true
ignoreUnknownKeys = true ignoreUnknownKeys = true
}) })
/*
* Refactor This
* */
suspend fun isInternetAvailable(): Boolean {
return withContext(dispatcherIO) {
try {
ktorHttpClient.head<String>("http://google.com")
true
} catch (e: Exception) {
println(e.message)
false
}
}
}
fun createHttpClient(enableNetworkLogs: Boolean = false,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient { fun createHttpClient(enableNetworkLogs: Boolean = false,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
install(JsonFeature) { install(JsonFeature) {

View File

@ -13,6 +13,8 @@ expect fun showPopUpMessage(text: String)
expect val dispatcherIO: CoroutineDispatcher expect val dispatcherIO: CoroutineDispatcher
expect val isInternetAvailable:Boolean
expect suspend fun downloadTracks( expect suspend fun downloadTracks(
list: List<TrackDetails>, list: List<TrackDetails>,
getYTIDBestMatch:suspend (String,TrackDetails)->String?, getYTIDBestMatch:suspend (String,TrackDetails)->String?,

View File

@ -12,7 +12,7 @@ import io.ktor.client.request.forms.*
import io.ktor.http.* import io.ktor.http.*
suspend fun authenticateSpotify(): TokenData? { suspend fun authenticateSpotify(): TokenData? {
return if(isInternetAvailable()) spotifyAuthClient.post("https://accounts.spotify.com/api/token"){ return if(isInternetAvailable) spotifyAuthClient.post("https://accounts.spotify.com/api/token"){
body = FormDataContent(Parameters.build { append("grant_type","client_credentials") }) body = FormDataContent(Parameters.build { append("grant_type","client_credentials") })
} else null } else null
} }

View File

@ -11,8 +11,12 @@ import com.github.kiulian.downloader.model.quality.AudioQuality
import com.shabinder.common.models.DownloadResult import com.shabinder.common.models.DownloadResult
import com.shabinder.common.models.DownloadStatus import com.shabinder.common.models.DownloadStatus
import com.shabinder.common.models.TrackDetails import com.shabinder.common.models.TrackDetails
import io.ktor.client.request.*
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.collect import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
actual fun openPlatform(packageID:String, platformLink:String){ actual fun openPlatform(packageID:String, platformLink:String){
//TODO //TODO
@ -26,21 +30,31 @@ actual fun giveDonation(){
//TODO //TODO
} }
@Composable
actual fun AlertDialog(
onDismissRequest: () -> Unit,
buttons: @Composable () -> Unit,
modifier: Modifier,
title: (@Composable () -> Unit)?,
text: @Composable (() -> Unit)?,
shape: Shape,
backgroundColor: Color,
contentColor: Color,
){}
actual fun queryActiveTracks(){} actual fun queryActiveTracks(){}
/*
* Refactor This
* */
private suspend fun isInternetAvailable(): Boolean {
return withContext(dispatcherIO) {
try {
ktorHttpClient.head<String>("http://google.com")
true
} catch (e: Exception) {
println(e.message)
false
}
}
}
actual val isInternetAvailable:Boolean
get(){
var result = false
val job = GlobalScope.launch { result = isInternetAvailable() }
while(job.isActive){}
return result
}
val DownloadProgressFlow: MutableSharedFlow<HashMap<String,DownloadStatus>> = MutableSharedFlow(1) val DownloadProgressFlow: MutableSharedFlow<HashMap<String,DownloadStatus>> = MutableSharedFlow(1)
actual suspend fun downloadTracks( actual suspend fun downloadTracks(

View File

@ -3,6 +3,8 @@ package com.shabinder.common.main.integration
import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.ImageBitmap
import com.arkivanov.decompose.ComponentContext import com.arkivanov.decompose.ComponentContext
import com.arkivanov.mvikotlin.extensions.coroutines.states import com.arkivanov.mvikotlin.extensions.coroutines.states
import com.shabinder.common.di.isInternetAvailable
import com.shabinder.common.di.showPopUpMessage
import com.shabinder.common.main.SpotiFlyerMain import com.shabinder.common.main.SpotiFlyerMain
import com.shabinder.common.main.SpotiFlyerMain.* import com.shabinder.common.main.SpotiFlyerMain.*
import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent import com.shabinder.common.main.store.SpotiFlyerMainStore.Intent
@ -26,7 +28,8 @@ internal class SpotiFlyerMainImpl(
override val models: Flow<State> = store.states override val models: Flow<State> = store.states
override fun onLinkSearch(link: String) { override fun onLinkSearch(link: String) {
mainOutput.callback(Output.Search(link = link)) if(isInternetAvailable) mainOutput.callback(Output.Search(link = link))
else showPopUpMessage("Check Network Connection Please")
} }
override fun onInputLinkChanged(link: String) { override fun onInputLinkChanged(link: String) {