mirror of
https://github.com/Shabinder/SpotiFlyer.git
synced 2024-11-22 09:04:32 +01:00
Android Network Connectivity
This commit is contained in:
parent
81a0b9a85c
commit
bf6463a3e1
@ -4,6 +4,9 @@ plugins {
|
|||||||
id("com.android.application")
|
id("com.android.application")
|
||||||
kotlin("android")
|
kotlin("android")
|
||||||
id("org.jetbrains.compose")
|
id("org.jetbrains.compose")
|
||||||
|
id("com.google.gms.google-services")
|
||||||
|
id("com.google.firebase.crashlytics")
|
||||||
|
id("com.google.firebase.firebase-perf")
|
||||||
}
|
}
|
||||||
|
|
||||||
group = "com.shabinder"
|
group = "com.shabinder"
|
||||||
@ -75,6 +78,12 @@ dependencies {
|
|||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
|
|
||||||
|
//Firebase
|
||||||
|
implementation(platform("com.google.firebase:firebase-bom:26.5.0"))
|
||||||
|
implementation("com.google.firebase:firebase-analytics-ktx")
|
||||||
|
implementation("com.google.firebase:firebase-crashlytics-ktx")
|
||||||
|
implementation("com.google.firebase:firebase-perf-ktx")
|
||||||
|
|
||||||
/*
|
/*
|
||||||
//Lifecycle
|
//Lifecycle
|
||||||
Versions.androidLifecycle.let{
|
Versions.androidLifecycle.let{
|
||||||
@ -85,6 +94,7 @@ dependencies {
|
|||||||
}
|
}
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
Extras.Android.apply {
|
Extras.Android.apply {
|
||||||
implementation(appUpdator)
|
implementation(appUpdator)
|
||||||
implementation(razorpay)
|
implementation(razorpay)
|
||||||
|
13
android/proguard-rules.pro
vendored
13
android/proguard-rules.pro
vendored
@ -21,6 +21,8 @@
|
|||||||
#-renamesourcefileattribute SourceFile
|
#-renamesourcefileattribute SourceFile
|
||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
|
-dontnote kotlinx.serialization.AnnotationsKt # core serialization annotations
|
||||||
|
-keepattributes SourceFile,LineNumberTable # Keep file names and line numbers.
|
||||||
|
-keep public class * extends java.lang.Exception # Optional: Keep custom exceptions.
|
||||||
|
|
||||||
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
# kotlinx-serialization-json specific. Add this if you have java.lang.NoClassDefFoundError kotlinx.serialization.json.JsonObjectSerializer
|
||||||
-keepclassmembers class kotlinx.serialization.json.** {
|
-keepclassmembers class kotlinx.serialization.json.** {
|
||||||
@ -29,11 +31,14 @@
|
|||||||
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
-keepclasseswithmembers class kotlinx.serialization.json.** {
|
||||||
kotlinx.serialization.KSerializer serializer(...);
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
}
|
}
|
||||||
|
-keep class com.shabinder.** { *; }
|
||||||
-keep,includedescriptorclasses class com.shabinder.spotiflyer.**$$serializer { *; } # <-- change package name to your app's
|
-keep,includedescriptorclasses class com.shabinder.**$$serializer { *; } # <-- change package name to your app's
|
||||||
-keepclassmembers class com.shabinder.spotiflyer.** { # <-- change package name to your app's
|
-keepclassmembers class com.shabinder.** { # <-- change package name to your app's
|
||||||
*** Companion;
|
*** Companion;
|
||||||
}
|
}
|
||||||
-keepclasseswithmembers class com.shabinder.spotiflyer.** { # <-- change package name to your app's
|
-keepclasseswithmembers class com.shabinder.** { # <-- change package name to your app's
|
||||||
kotlinx.serialization.KSerializer serializer(...);
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
}
|
}
|
||||||
|
# Ktor
|
||||||
|
-keep class io.ktor.** { *; }
|
||||||
|
-keep class kotlinx.coroutines.** { *; }
|
@ -12,6 +12,7 @@
|
|||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
<uses-permission android:name="android.permission.GET_ACCOUNTS" />
|
||||||
|
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
@ -45,9 +45,7 @@ import com.shabinder.common.root.SpotiFlyerRoot
|
|||||||
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
import com.shabinder.common.root.callbacks.SpotiFlyerRootCallBacks
|
||||||
import com.shabinder.common.uikit.*
|
import com.shabinder.common.uikit.*
|
||||||
import com.shabinder.database.Database
|
import com.shabinder.database.Database
|
||||||
import com.shabinder.spotiflyer.utils.checkIfLatestVersion
|
import com.shabinder.spotiflyer.utils.*
|
||||||
import com.shabinder.spotiflyer.utils.disableDozeMode
|
|
||||||
import com.shabinder.spotiflyer.utils.requestStoragePermission
|
|
||||||
import com.tonyodev.fetch2.Status
|
import com.tonyodev.fetch2.Status
|
||||||
import kotlinx.coroutines.*
|
import kotlinx.coroutines.*
|
||||||
import kotlinx.coroutines.flow.*
|
import kotlinx.coroutines.flow.*
|
||||||
@ -100,6 +98,7 @@ class MainActivity : ComponentActivity(), PaymentResultListener {
|
|||||||
|
|
||||||
if(askForPermission && !permissionGranted.value) permissionDialog()
|
if(askForPermission && !permissionGranted.value) permissionDialog()
|
||||||
|
|
||||||
|
NetworkDialog()
|
||||||
root = SpotiFlyerRootContent(rememberRootComponent(::spotiFlyerRoot),statusBarHeight)
|
root = SpotiFlyerRootContent(rememberRootComponent(::spotiFlyerRoot),statusBarHeight)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,73 @@
|
|||||||
|
package com.shabinder.spotiflyer.utils
|
||||||
|
|
||||||
|
import androidx.compose.animation.Crossfade
|
||||||
|
import androidx.compose.foundation.Image
|
||||||
|
import androidx.compose.foundation.layout.*
|
||||||
|
import androidx.compose.material.AlertDialog
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.rounded.CloudOff
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
|
import androidx.compose.ui.text.style.TextAlign
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import com.shabinder.common.di.isInternetAvailable
|
||||||
|
import com.shabinder.common.di.isInternetAvailableState
|
||||||
|
import com.shabinder.common.uikit.SpotiFlyerShapes
|
||||||
|
import com.shabinder.common.uikit.SpotiFlyerTypography
|
||||||
|
import com.shabinder.common.uikit.colorOffWhite
|
||||||
|
import kotlinx.coroutines.delay
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun NetworkDialog(
|
||||||
|
networkAvailability: State<Boolean?> = isInternetAvailableState()
|
||||||
|
){
|
||||||
|
var visible by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
|
LaunchedEffect(Unit){
|
||||||
|
delay(2600)
|
||||||
|
visible = true
|
||||||
|
}
|
||||||
|
if(networkAvailability.value == false){
|
||||||
|
Crossfade(visible){
|
||||||
|
if(it){
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = {},
|
||||||
|
buttons = {
|
||||||
|
/* TextButton({
|
||||||
|
//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),
|
||||||
|
){
|
||||||
|
Text("Retry",color = Color.Black,fontSize = 18.sp,textAlign = TextAlign.Center)
|
||||||
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -16,6 +16,9 @@ repositories {
|
|||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation("com.android.tools.build:gradle:4.0.1")
|
implementation("com.android.tools.build:gradle:4.0.1")
|
||||||
|
implementation("com.google.gms:google-services:4.3.5")
|
||||||
|
implementation("com.google.firebase:perf-plugin:1.3.4")
|
||||||
|
implementation("com.google.firebase:firebase-crashlytics-gradle:2.5.0")
|
||||||
implementation(JetBrains.Compose.gradlePlugin)
|
implementation(JetBrains.Compose.gradlePlugin)
|
||||||
implementation(JetBrains.Kotlin.gradlePlugin)
|
implementation(JetBrains.Kotlin.gradlePlugin)
|
||||||
implementation(JetBrains.Kotlin.serialization)
|
implementation(JetBrains.Kotlin.serialization)
|
||||||
|
@ -27,7 +27,7 @@ object Versions {
|
|||||||
//Android
|
//Android
|
||||||
const val versionCode = 15
|
const val versionCode = 15
|
||||||
const val minSdkVersion = 24
|
const val minSdkVersion = 24
|
||||||
const val compileSdkVersion = 30
|
const val compileSdkVersion = 29
|
||||||
const val targetSdkVersion = 29
|
const val targetSdkVersion = 29
|
||||||
const val androidLifecycle = "2.3.0"
|
const val androidLifecycle = "2.3.0"
|
||||||
}
|
}
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.uikit"/>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.models"/>
|
||||||
|
@ -1,3 +1,5 @@
|
|||||||
|
import org.jetbrains.compose.compose
|
||||||
|
|
||||||
plugins {
|
plugins {
|
||||||
id("multiplatform-compose-setup")
|
id("multiplatform-compose-setup")
|
||||||
id("android-setup")
|
id("android-setup")
|
||||||
@ -8,6 +10,7 @@ kotlin {
|
|||||||
sourceSets {
|
sourceSets {
|
||||||
commonMain {
|
commonMain {
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation(compose.materialIconsExtended)
|
||||||
implementation(project(":common:data-models"))
|
implementation(project(":common:data-models"))
|
||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":fuzzywuzzy:app"))
|
implementation(project(":fuzzywuzzy:app"))
|
||||||
|
@ -1,2 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.di" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||||
|
</manifest>
|
||||||
|
@ -4,8 +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
|
||||||
|
@ -0,0 +1,122 @@
|
|||||||
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Context.CONNECTIVITY_SERVICE
|
||||||
|
import android.net.ConnectivityManager
|
||||||
|
import android.net.Network
|
||||||
|
import android.net.NetworkCapabilities.NET_CAPABILITY_INTERNET
|
||||||
|
import android.net.NetworkRequest
|
||||||
|
import android.util.Log
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
|
import androidx.compose.runtime.State
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import com.shabinder.common.database.appContext
|
||||||
|
import kotlinx.coroutines.CoroutineScope
|
||||||
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
|
import java.io.IOException
|
||||||
|
import java.net.InetSocketAddress
|
||||||
|
import javax.net.SocketFactory
|
||||||
|
|
||||||
|
const val TAG = "C-Manager"
|
||||||
|
|
||||||
|
val isInternetAvailable by lazy { ConnectionLiveData(appContext) }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun isInternetAvailableState(): State<Boolean?>{
|
||||||
|
return isInternetAvailable.observeAsState()
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Save all available networks with an internet connection to a set (@validNetworks).
|
||||||
|
* As long as the size of the set > 0, this LiveData emits true.
|
||||||
|
* MinSdk = 21.
|
||||||
|
*
|
||||||
|
* Inspired by:
|
||||||
|
* https://github.com/AlexSheva-mason/Rick-Morty-Database/blob/master/app/src/main/java/com/shevaalex/android/rickmortydatabase/utils/networking/ConnectionLiveData.kt
|
||||||
|
*/
|
||||||
|
class ConnectionLiveData(context: Context = appContext) : LiveData<Boolean>() {
|
||||||
|
|
||||||
|
private lateinit var networkCallback: ConnectivityManager.NetworkCallback
|
||||||
|
private val cm = context.getSystemService(CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||||
|
private val validNetworks: MutableSet<Network> = HashSet()
|
||||||
|
|
||||||
|
private fun checkValidNetworks() {
|
||||||
|
postValue(validNetworks.size > 0)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onActive() {
|
||||||
|
checkValidNetworks()
|
||||||
|
networkCallback = createNetworkCallback()
|
||||||
|
val networkRequest = NetworkRequest.Builder()
|
||||||
|
.addCapability(NET_CAPABILITY_INTERNET)
|
||||||
|
.build()
|
||||||
|
cm.registerNetworkCallback(networkRequest, networkCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onInactive() {
|
||||||
|
cm.unregisterNetworkCallback(networkCallback)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun createNetworkCallback() = object : ConnectivityManager.NetworkCallback() {
|
||||||
|
|
||||||
|
/*
|
||||||
|
Called when a network is detected. If that network has internet, save it in the Set.
|
||||||
|
Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onAvailable(android.net.Network)
|
||||||
|
*/
|
||||||
|
override fun onAvailable(network: Network) {
|
||||||
|
Log.d(TAG, "onAvailable: ${network}")
|
||||||
|
val networkCapabilities = cm.getNetworkCapabilities(network)
|
||||||
|
val hasInternetCapability = networkCapabilities?.hasCapability(NET_CAPABILITY_INTERNET)
|
||||||
|
Log.d(TAG, "onAvailable: ${network}, $hasInternetCapability")
|
||||||
|
if (hasInternetCapability == true) {
|
||||||
|
// check if this network actually has internet
|
||||||
|
CoroutineScope(Dispatchers.IO).launch {
|
||||||
|
val hasInternet = DoesNetworkHaveInternet.execute(network.socketFactory)
|
||||||
|
if(hasInternet){
|
||||||
|
withContext(Dispatchers.Main){
|
||||||
|
Log.d(TAG, "onAvailable: adding network. ${network}")
|
||||||
|
validNetworks.add(network)
|
||||||
|
checkValidNetworks()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
If the callback was registered with registerNetworkCallback() it will be called for each network which no longer satisfies the criteria of the callback.
|
||||||
|
Source: https://developer.android.com/reference/android/net/ConnectivityManager.NetworkCallback#onLost(android.net.Network)
|
||||||
|
*/
|
||||||
|
override fun onLost(network: Network) {
|
||||||
|
Log.d(TAG, "onLost: ${network}")
|
||||||
|
validNetworks.remove(network)
|
||||||
|
checkValidNetworks()
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Send a ping to googles primary DNS.
|
||||||
|
* If successful, that means we have internet.
|
||||||
|
*/
|
||||||
|
object DoesNetworkHaveInternet {
|
||||||
|
|
||||||
|
// Make sure to execute this on a background thread.
|
||||||
|
fun execute(socketFactory: SocketFactory): Boolean {
|
||||||
|
return try{
|
||||||
|
Log.d(TAG, "PINGING google.")
|
||||||
|
val socket = socketFactory.createSocket() ?: throw IOException("Socket is null.")
|
||||||
|
socket.connect(InetSocketAddress("8.8.8.8", 53), 1500)
|
||||||
|
socket.close()
|
||||||
|
Log.d(TAG, "PING success.")
|
||||||
|
true
|
||||||
|
}catch (e: IOException){
|
||||||
|
Log.e(TAG, "No internet connection. $e")
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,33 @@
|
|||||||
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import androidx.compose.runtime.*
|
||||||
|
import androidx.compose.ui.platform.LocalLifecycleOwner
|
||||||
|
import androidx.lifecycle.Lifecycle
|
||||||
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.LiveData
|
||||||
|
import androidx.lifecycle.Observer
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> LiveData<T>.observeAsState(): State<T?> = observeAsState(value)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts observing this [LiveData] and represents its values via [State]. Every time there would
|
||||||
|
* be new value posted into the [LiveData] the returned [State] will be updated causing
|
||||||
|
* recomposition of every [State.value] usage.
|
||||||
|
*
|
||||||
|
* The inner observer will automatically be removed when this composable disposes or the current
|
||||||
|
* [LifecycleOwner] moves to the [Lifecycle.State.DESTROYED] state.
|
||||||
|
*
|
||||||
|
* @sample androidx.compose.runtime.livedata.samples.LiveDataWithInitialSample
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun <R, T : R> LiveData<T>.observeAsState(initial: R): State<R> {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val state = remember { mutableStateOf(initial) }
|
||||||
|
DisposableEffect(this, lifecycleOwner) {
|
||||||
|
val observer = Observer<T> { state.value = it }
|
||||||
|
observe(lifecycleOwner, observer)
|
||||||
|
onDispose { removeObserver(observer) }
|
||||||
|
}
|
||||||
|
return state
|
||||||
|
}
|
@ -14,6 +14,7 @@ import io.ktor.client.features.logging.*
|
|||||||
import io.ktor.client.request.*
|
import io.ktor.client.request.*
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
|
import kotlinx.coroutines.withContext
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
import org.koin.core.context.startKoin
|
import org.koin.core.context.startKoin
|
||||||
import org.koin.dsl.KoinAppDeclaration
|
import org.koin.dsl.KoinAppDeclaration
|
||||||
@ -46,20 +47,18 @@ val kotlinxSerializer = KotlinxSerializer( Json {
|
|||||||
/*
|
/*
|
||||||
* Refactor This
|
* Refactor This
|
||||||
* */
|
* */
|
||||||
fun isInternetAvailable(): Boolean {
|
suspend fun isInternetAvailable(): Boolean {
|
||||||
var result = false
|
return withContext(dispatcherIO) {
|
||||||
val job = GlobalScope.launch {
|
|
||||||
try {
|
try {
|
||||||
ktorHttpClient.head<String>("http://google.com")
|
ktorHttpClient.head<String>("http://google.com")
|
||||||
result = true
|
true
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
println(e.message)
|
println(e.message)
|
||||||
result = false
|
false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
while (job.isActive){}
|
|
||||||
return result
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fun createHttpClient(enableNetworkLogs: Boolean = false,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
|
fun createHttpClient(enableNetworkLogs: Boolean = false,serializer: KotlinxSerializer = kotlinxSerializer) = HttpClient {
|
||||||
install(JsonFeature) {
|
install(JsonFeature) {
|
||||||
this.serializer = serializer
|
this.serializer = serializer
|
||||||
|
@ -0,0 +1,6 @@
|
|||||||
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
sealed class NetworkResponse<out T> {
|
||||||
|
data class Success<T>(val value:T):NetworkResponse<T>()
|
||||||
|
data class Error(val message:String):NetworkResponse<Nothing>()
|
||||||
|
}
|
@ -21,7 +21,7 @@ class TokenStore(
|
|||||||
db.add(token.access_token!!, token.expiry!! + Clock.System.now().epochSeconds)
|
db.add(token.access_token!!, token.expiry!! + Clock.System.now().epochSeconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun getToken(): TokenData {
|
suspend fun getToken(): TokenData? {
|
||||||
var token: TokenData? = db.select().executeAsOneOrNull()?.let {
|
var token: TokenData? = db.select().executeAsOneOrNull()?.let {
|
||||||
TokenData(it.accessToken,null,it.expiry)
|
TokenData(it.accessToken,null,it.expiry)
|
||||||
}
|
}
|
||||||
@ -29,7 +29,7 @@ class TokenStore(
|
|||||||
if(Clock.System.now().epochSeconds > token?.expiry ?:0 || token == null){
|
if(Clock.System.now().epochSeconds > token?.expiry ?:0 || token == null){
|
||||||
logger.d{"Requesting New Token"}
|
logger.d{"Requesting New Token"}
|
||||||
token = authenticateSpotify()
|
token = authenticateSpotify()
|
||||||
GlobalScope.launch { token.access_token?.let { save(token) } }
|
GlobalScope.launch { token?.access_token?.let { save(token) } }
|
||||||
}
|
}
|
||||||
return token
|
return token
|
||||||
}
|
}
|
||||||
|
@ -18,10 +18,7 @@ package com.shabinder.common.di.providers
|
|||||||
|
|
||||||
import co.touchlab.kermit.Kermit
|
import co.touchlab.kermit.Kermit
|
||||||
import com.shabinder.common.database.DownloadRecordDatabaseQueries
|
import com.shabinder.common.database.DownloadRecordDatabaseQueries
|
||||||
import com.shabinder.common.di.Dir
|
import com.shabinder.common.di.*
|
||||||
import com.shabinder.common.di.TokenStore
|
|
||||||
import com.shabinder.common.di.finalOutputDir
|
|
||||||
import com.shabinder.common.di.kotlinxSerializer
|
|
||||||
import com.shabinder.common.di.spotify.SpotifyRequests
|
import com.shabinder.common.di.spotify.SpotifyRequests
|
||||||
import com.shabinder.common.models.PlatformQueryResult
|
import com.shabinder.common.models.PlatformQueryResult
|
||||||
import com.shabinder.common.models.TrackDetails
|
import com.shabinder.common.models.TrackDetails
|
||||||
@ -48,8 +45,17 @@ class SpotifyProvider(
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
logger.d { "Creating Spotify Provider" }
|
logger.d { "Creating Spotify Provider" }
|
||||||
GlobalScope.launch(Dispatchers.Default) {
|
GlobalScope.launch(Dispatchers.Default) {authenticateSpotify()}
|
||||||
val token = tokenStore.getToken()
|
}
|
||||||
|
|
||||||
|
override suspend fun authenticateSpotify(): HttpClient?{
|
||||||
|
val token = tokenStore.getToken()
|
||||||
|
return if(token == null) {
|
||||||
|
showPopUpMessage("Please Check your Network Connection")
|
||||||
|
null
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
logger.d { "Spotify Provider Created with $token" }
|
||||||
httpClient = HttpClient {
|
httpClient = HttpClient {
|
||||||
defaultRequest {
|
defaultRequest {
|
||||||
header("Authorization","Bearer ${token.access_token}")
|
header("Authorization","Bearer ${token.access_token}")
|
||||||
@ -58,7 +64,7 @@ class SpotifyProvider(
|
|||||||
serializer = kotlinxSerializer
|
serializer = kotlinxSerializer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
logger.d { "Spotify Provider Created with $token" }
|
httpClient
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -68,6 +74,11 @@ class SpotifyProvider(
|
|||||||
get() = database.downloadRecordDatabaseQueries
|
get() = database.downloadRecordDatabaseQueries
|
||||||
|
|
||||||
suspend fun query(fullLink: String): PlatformQueryResult?{
|
suspend fun query(fullLink: String): PlatformQueryResult?{
|
||||||
|
|
||||||
|
if(!this::httpClient.isInitialized){
|
||||||
|
authenticateSpotify()
|
||||||
|
}
|
||||||
|
|
||||||
var spotifyLink =
|
var spotifyLink =
|
||||||
"https://" + fullLink.substringAfterLast("https://").substringBefore(" ").trim()
|
"https://" + fullLink.substringAfterLast("https://").substringBefore(" ").trim()
|
||||||
|
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package com.shabinder.common.di.spotify
|
package com.shabinder.common.di.spotify
|
||||||
|
|
||||||
|
import com.shabinder.common.di.isInternetAvailable
|
||||||
import com.shabinder.common.di.kotlinxSerializer
|
import com.shabinder.common.di.kotlinxSerializer
|
||||||
import com.shabinder.common.models.spotify.TokenData
|
import com.shabinder.common.models.spotify.TokenData
|
||||||
import io.ktor.client.*
|
import io.ktor.client.*
|
||||||
@ -10,10 +11,10 @@ import io.ktor.client.request.*
|
|||||||
import io.ktor.client.request.forms.*
|
import io.ktor.client.request.forms.*
|
||||||
import io.ktor.http.*
|
import io.ktor.http.*
|
||||||
|
|
||||||
suspend fun authenticateSpotify(): TokenData {
|
suspend fun authenticateSpotify(): TokenData? {
|
||||||
return 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
|
||||||
}
|
}
|
||||||
|
|
||||||
private val spotifyAuthClient by lazy {
|
private val spotifyAuthClient by lazy {
|
||||||
|
@ -13,6 +13,8 @@ interface SpotifyRequests {
|
|||||||
|
|
||||||
val httpClient:HttpClient
|
val httpClient:HttpClient
|
||||||
|
|
||||||
|
suspend fun authenticateSpotify():HttpClient?
|
||||||
|
|
||||||
suspend fun getPlaylist(playlistID: String): Playlist {
|
suspend fun getPlaylist(playlistID: String): Playlist {
|
||||||
return httpClient.get("$BASE_URL/playlists/$playlistID")
|
return httpClient.get("$BASE_URL/playlists/$playlistID")
|
||||||
}
|
}
|
||||||
|
@ -1,5 +1,9 @@
|
|||||||
package com.shabinder.common.di
|
package com.shabinder.common.di
|
||||||
|
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.Shape
|
||||||
import com.github.kiulian.downloader.YoutubeDownloader
|
import com.github.kiulian.downloader.YoutubeDownloader
|
||||||
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
|
||||||
@ -22,6 +26,19 @@ 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(){}
|
||||||
|
|
||||||
val DownloadProgressFlow: MutableSharedFlow<HashMap<String,DownloadStatus>> = MutableSharedFlow(1)
|
val DownloadProgressFlow: MutableSharedFlow<HashMap<String,DownloadStatus>> = MutableSharedFlow(1)
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.list"/>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.main"/>
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest package="com.shabinder.common.ui"/>
|
<manifest package="com.shabinder.common.root"/>
|
||||||
|
@ -22,6 +22,7 @@ kotlin {
|
|||||||
implementation(project(":common:database"))
|
implementation(project(":common:database"))
|
||||||
implementation(project(":common:dependency-injection"))
|
implementation(project(":common:dependency-injection"))
|
||||||
implementation(project(":common:compose"))
|
implementation(project(":common:compose"))
|
||||||
|
implementation(project(":common:root"))
|
||||||
implementation(Decompose.decompose)
|
implementation(Decompose.decompose)
|
||||||
implementation(Decompose.extensionsCompose)
|
implementation(Decompose.extensionsCompose)
|
||||||
implementation(MVIKotlin.mvikotlin)
|
implementation(MVIKotlin.mvikotlin)
|
||||||
|
Loading…
Reference in New Issue
Block a user