mirror of
https://github.com/wgtunnel/android.git
synced 2026-07-03 14:07:49 +02:00
Compare commits
4 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 69b07eec6f | |||
| e81066f508 | |||
| 64bb9f3b82 | |||
| c1b560e822 |
@@ -13,6 +13,7 @@ WG Tunnel
|
||||
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=com.zaneschepke.wireguardautotunnel)
|
||||
[](https://www.amazon.com/gp/product/B0CFGGL7WK)
|
||||
|
||||
</span>
|
||||
|
||||
|
||||
@@ -17,12 +17,12 @@ android {
|
||||
|
||||
val versionMajor = 2
|
||||
val versionMinor = 4
|
||||
val versionPatch = 2
|
||||
val versionPatch = 5
|
||||
val versionBuild = 0
|
||||
|
||||
defaultConfig {
|
||||
applicationId = "com.zaneschepke.wireguardautotunnel"
|
||||
minSdk = 28
|
||||
minSdk = 26
|
||||
targetSdk = 34
|
||||
versionCode = versionMajor * 10000 + versionMinor * 1000 + versionPatch * 100 + versionBuild
|
||||
versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
|
||||
@@ -64,8 +64,8 @@ android {
|
||||
}
|
||||
|
||||
dependencies {
|
||||
implementation("androidx.core:core-ktx:1.10.1")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.1")
|
||||
implementation("androidx.core:core-ktx:1.12.0")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.6.2")
|
||||
implementation("androidx.activity:activity-compose:1.7.2")
|
||||
implementation(platform("androidx.compose:compose-bom:2023.03.00"))
|
||||
implementation("androidx.compose.ui:ui")
|
||||
@@ -89,7 +89,7 @@ dependencies {
|
||||
implementation("com.jakewharton.timber:timber:5.0.1")
|
||||
|
||||
// compose navigation
|
||||
implementation("androidx.navigation:navigation-compose:2.7.1")
|
||||
implementation("androidx.navigation:navigation-compose:2.7.2")
|
||||
implementation("androidx.hilt:hilt-navigation-compose:1.0.0")
|
||||
|
||||
// hilt
|
||||
@@ -107,10 +107,10 @@ dependencies {
|
||||
implementation("io.objectbox:objectbox-kotlin:${rExtra.get("objectBoxVersion")}")
|
||||
|
||||
//lifecycle
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.1")
|
||||
implementation("androidx.lifecycle:lifecycle-runtime-compose:2.6.2")
|
||||
|
||||
//icons
|
||||
implementation("androidx.compose.material:material-icons-extended:1.5.0")
|
||||
implementation("androidx.compose.material:material-icons-extended:1.5.1")
|
||||
|
||||
|
||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.5.1")
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import android.content.pm.PackageManager
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.google.mlkit.common.MlKit
|
||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
@@ -22,6 +23,11 @@ class WireGuardAutoTunnel : Application() {
|
||||
FirebaseCrashlytics.getInstance().setCrashlyticsCollectionEnabled(false);
|
||||
Timber.plant(Timber.DebugTree())
|
||||
}
|
||||
try {
|
||||
MlKit.initialize(this)
|
||||
} catch (e : Exception) {
|
||||
Timber.e(e.message)
|
||||
}
|
||||
settingsRepo.init()
|
||||
}
|
||||
|
||||
|
||||
@@ -2,5 +2,6 @@ package com.zaneschepke.wireguardautotunnel.service.foreground
|
||||
|
||||
enum class Action {
|
||||
START,
|
||||
START_FOREGROUND,
|
||||
STOP
|
||||
}
|
||||
+1
-1
@@ -22,7 +22,7 @@ open class ForegroundService : Service() {
|
||||
val action = intent.action
|
||||
Timber.d("using an intent with action $action")
|
||||
when (action) {
|
||||
Action.START.name -> startService(intent.extras)
|
||||
Action.START.name, Action.START_FOREGROUND.name -> startService(intent.extras)
|
||||
Action.STOP.name -> stopService(intent.extras)
|
||||
"android.net.VpnService" -> {
|
||||
Timber.d("Always-on VPN starting service")
|
||||
|
||||
+28
-8
@@ -1,7 +1,6 @@
|
||||
package com.zaneschepke.wireguardautotunnel.service.foreground
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.app.Application
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Context.ACTIVITY_SERVICE
|
||||
@@ -9,7 +8,6 @@ import android.content.Intent
|
||||
import com.google.firebase.crashlytics.ktx.crashlytics
|
||||
import com.google.firebase.ktx.Firebase
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import timber.log.Timber
|
||||
|
||||
object ServiceManager {
|
||||
@Suppress("DEPRECATION")
|
||||
@@ -36,13 +34,11 @@ object ServiceManager {
|
||||
intent.component?.javaClass
|
||||
try {
|
||||
when(action) {
|
||||
Action.START_FOREGROUND -> {
|
||||
context.startForegroundService(intent)
|
||||
}
|
||||
Action.START -> {
|
||||
try {
|
||||
context.startForegroundService(intent)
|
||||
} catch (e : Exception) {
|
||||
Timber.e("Unable to start service foreground ${e.message}")
|
||||
context.startService(intent)
|
||||
}
|
||||
context.startService(intent)
|
||||
}
|
||||
Action.STOP -> context.startService(intent)
|
||||
}
|
||||
@@ -66,6 +62,22 @@ object ServiceManager {
|
||||
)
|
||||
}
|
||||
|
||||
fun startVpnServiceForeground(context : Context, tunnelConfig : String) {
|
||||
actionOnService(
|
||||
Action.START_FOREGROUND,
|
||||
context,
|
||||
WireGuardTunnelService::class.java,
|
||||
mapOf(context.getString(R.string.tunnel_extras_key) to tunnelConfig))
|
||||
}
|
||||
|
||||
private fun startWatcherServiceForeground(context : Context, tunnelConfig : String) {
|
||||
actionOnService(
|
||||
Action.START, context,
|
||||
WireGuardConnectivityWatcherService::class.java, mapOf(context.
|
||||
getString(R.string.tunnel_extras_key) to
|
||||
tunnelConfig))
|
||||
}
|
||||
|
||||
fun startWatcherService(context : Context, tunnelConfig : String) {
|
||||
actionOnService(
|
||||
Action.START, context,
|
||||
@@ -87,4 +99,12 @@ object ServiceManager {
|
||||
ServiceState.STOPPED -> startWatcherService(context, tunnelConfig)
|
||||
}
|
||||
}
|
||||
|
||||
fun toggleWatcherServiceForeground(context: Context, tunnelConfig : String) {
|
||||
when(getServiceState( context,
|
||||
WireGuardConnectivityWatcherService::class.java,)) {
|
||||
ServiceState.STARTED -> stopWatcherService(context)
|
||||
ServiceState.STOPPED -> startWatcherServiceForeground(context, tunnelConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
+9
-2
@@ -59,8 +59,16 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||
private val tag = this.javaClass.name;
|
||||
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
launchWatcherNotification()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startService(extras: Bundle?) {
|
||||
super.startService(extras)
|
||||
launchWatcherNotification()
|
||||
val tunnelId = extras?.getString(getString(R.string.tunnel_extras_key))
|
||||
if (tunnelId != null) {
|
||||
this.tunnelConfig = tunnelId
|
||||
@@ -68,7 +76,6 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||
// we need this lock so our service gets not affected by Doze Mode
|
||||
initWakeLock()
|
||||
cancelWatcherJob()
|
||||
launchWatcherNotification()
|
||||
if(this::tunnelConfig.isInitialized) {
|
||||
startWatcherJob()
|
||||
} else {
|
||||
@@ -181,7 +188,7 @@ class WireGuardConnectivityWatcherService : ForegroundService() {
|
||||
}
|
||||
|
||||
private suspend fun manageVpn() {
|
||||
while(watcherJob.isActive) {
|
||||
while(true) {
|
||||
if(setting.isTunnelOnMobileDataEnabled &&
|
||||
!isWifiConnected &&
|
||||
isMobileDataConnected
|
||||
|
||||
+9
-3
@@ -37,8 +37,16 @@ class WireGuardTunnelService : ForegroundService() {
|
||||
|
||||
private var tunnelName : String = ""
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
CoroutineScope(Dispatchers.Main).launch {
|
||||
launchVpnStartingNotification()
|
||||
}
|
||||
}
|
||||
|
||||
override fun startService(extras : Bundle?) {
|
||||
super.startService(extras)
|
||||
launchVpnStartingNotification()
|
||||
val tunnelConfigString = extras?.getString(getString(R.string.tunnel_extras_key))
|
||||
cancelJob()
|
||||
job = CoroutineScope(Dispatchers.IO).launch {
|
||||
@@ -47,7 +55,6 @@ class WireGuardTunnelService : ForegroundService() {
|
||||
val tunnelConfig = TunnelConfig.from(tunnelConfigString)
|
||||
tunnelName = tunnelConfig.name
|
||||
vpnService.startTunnel(tunnelConfig)
|
||||
launchVpnStartingNotification()
|
||||
} catch (e : Exception) {
|
||||
Timber.e("Problem starting tunnel: ${e.message}")
|
||||
stopService(extras)
|
||||
@@ -61,7 +68,6 @@ class WireGuardTunnelService : ForegroundService() {
|
||||
val tunnelConfig = TunnelConfig.from(setting.defaultTunnel!!)
|
||||
tunnelName = tunnelConfig.name
|
||||
vpnService.startTunnel(tunnelConfig)
|
||||
launchVpnStartingNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +106,7 @@ class WireGuardTunnelService : ForegroundService() {
|
||||
|
||||
override fun stopService(extras : Bundle?) {
|
||||
super.stopService(extras)
|
||||
CoroutineScope(Dispatchers.IO).launch() {
|
||||
CoroutineScope(Dispatchers.IO).launch {
|
||||
vpnService.stopTunnel()
|
||||
}
|
||||
cancelJob()
|
||||
|
||||
+26
-1
@@ -3,19 +3,44 @@ package com.zaneschepke.wireguardautotunnel.service.shortcut
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.zaneschepke.wireguardautotunnel.R
|
||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.Action
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.ServiceManager
|
||||
import com.zaneschepke.wireguardautotunnel.service.foreground.WireGuardTunnelService
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class ShortcutsActivity : AppCompatActivity() {
|
||||
|
||||
@Inject
|
||||
lateinit var settingsRepo : Repository<Settings>
|
||||
|
||||
private val scope = CoroutineScope(Dispatchers.Main);
|
||||
|
||||
private fun attemptWatcherServiceToggle(tunnelConfig : String) {
|
||||
scope.launch {
|
||||
val settings = settingsRepo.getAll()
|
||||
if (!settings.isNullOrEmpty()) {
|
||||
val setting = settings.first()
|
||||
if(setting.isAutoTunnelEnabled) {
|
||||
ServiceManager.toggleWatcherServiceForeground(this@ShortcutsActivity, tunnelConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
if(intent.getStringExtra(ShortcutsManager.CLASS_NAME_EXTRA_KEY)
|
||||
.equals(WireGuardTunnelService::class.java.name)) {
|
||||
|
||||
intent.getStringExtra(getString(R.string.tunnel_extras_key))?.let {
|
||||
ServiceManager.toggleWatcherService(this, it)
|
||||
attemptWatcherServiceToggle(it)
|
||||
}
|
||||
when(intent.action){
|
||||
Action.STOP.name -> ServiceManager.stopVpnService(this)
|
||||
|
||||
+2
-2
@@ -66,7 +66,7 @@ class TunnelControlTile : TileService() {
|
||||
if(vpnService.getState() == Tunnel.State.UP) {
|
||||
ServiceManager.stopVpnService(this@TunnelControlTile)
|
||||
} else {
|
||||
ServiceManager.startVpnService(this@TunnelControlTile, tunnel.toString())
|
||||
ServiceManager.startVpnServiceForeground(this@TunnelControlTile, tunnel.toString())
|
||||
}
|
||||
}
|
||||
} catch (e : Exception) {
|
||||
@@ -100,7 +100,7 @@ class TunnelControlTile : TileService() {
|
||||
if (!settings.isNullOrEmpty()) {
|
||||
val setting = settings.first()
|
||||
if(setting.isAutoTunnelEnabled) {
|
||||
ServiceManager.toggleWatcherService(this@TunnelControlTile, tunnelConfig)
|
||||
ServiceManager.toggleWatcherServiceForeground(this@TunnelControlTile, tunnelConfig)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -10,6 +10,7 @@ import androidx.compose.runtime.toMutableStateList
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.zaneschepke.wireguardautotunnel.repository.Repository
|
||||
import com.zaneschepke.wireguardautotunnel.service.shortcut.ShortcutsManager
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.Settings
|
||||
import com.zaneschepke.wireguardautotunnel.service.tunnel.model.TunnelConfig
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
@@ -114,12 +115,14 @@ class ConfigViewModel @Inject constructor(private val application : Application,
|
||||
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
|
||||
packageManager.getPackagesHoldingPermissions(permissions, PackageManager.PackageInfoFlags.of(0L))
|
||||
} else {
|
||||
@Suppress("DEPRECATION")
|
||||
packageManager.getPackagesHoldingPermissions(permissions, 0)
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun onSaveAllChanges() {
|
||||
if(_tunnel.value != null) {
|
||||
ShortcutsManager.removeTunnelShortcuts(application, _tunnel.value!!)
|
||||
}
|
||||
var wgQuick = _tunnel.value?.wgQuick
|
||||
if(wgQuick != null) {
|
||||
wgQuick = if(_include.value) {
|
||||
@@ -135,6 +138,7 @@ class ConfigViewModel @Inject constructor(private val application : Application,
|
||||
wgQuick = wgQuick
|
||||
)?.let {
|
||||
tunnelRepo.save(it)
|
||||
ShortcutsManager.createTunnelShortcuts(application, it)
|
||||
val settings = settingsRepo.getAll()
|
||||
if(settings != null) {
|
||||
val setting = settings[0]
|
||||
|
||||
+13
-9
@@ -1,6 +1,7 @@
|
||||
package com.zaneschepke.wireguardautotunnel.ui.screens.main
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
@@ -17,7 +18,6 @@ import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.lazy.itemsIndexed
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
@@ -103,6 +103,7 @@ fun MainScreen(
|
||||
val state by viewModel.state.collectAsStateWithLifecycle(Tunnel.State.DOWN)
|
||||
val tunnelName by viewModel.tunnelName.collectAsStateWithLifecycle("")
|
||||
|
||||
|
||||
// Nested scroll for control FAB
|
||||
val nestedScrollConnection = remember {
|
||||
object : NestedScrollConnection {
|
||||
@@ -135,11 +136,9 @@ fun MainScreen(
|
||||
}
|
||||
|
||||
val pickFileLauncher = rememberLauncherForActivityResult(
|
||||
ActivityResultContracts.GetContent()
|
||||
) { file ->
|
||||
if (file != null) {
|
||||
viewModel.onTunnelFileSelected(file)
|
||||
}
|
||||
ActivityResultContracts.StartActivityForResult()
|
||||
) { result ->
|
||||
result.data?.data?.let { viewModel.onTunnelFileSelected(it) }
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
@@ -196,7 +195,11 @@ fun MainScreen(
|
||||
.fillMaxWidth()
|
||||
.clickable {
|
||||
showBottomSheet = false
|
||||
pickFileLauncher.launch("*/*")
|
||||
val fileSelectionIntent = Intent(Intent.ACTION_OPEN_DOCUMENT).apply {
|
||||
addCategory(Intent.CATEGORY_OPENABLE)
|
||||
type = "*/*"
|
||||
}
|
||||
pickFileLauncher.launch(fileSelectionIntent)
|
||||
}
|
||||
.padding(10.dp)
|
||||
) {
|
||||
@@ -242,10 +245,11 @@ fun MainScreen(
|
||||
) {
|
||||
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.nestedScroll(nestedScrollConnection),
|
||||
) {
|
||||
itemsIndexed(tunnels.toList()) { index, tunnel ->
|
||||
itemsIndexed(tunnels.toList()) { _, tunnel ->
|
||||
val focusRequester = FocusRequester();
|
||||
RowListItem(leadingIcon = Icons.Rounded.Circle,
|
||||
leadingIconColor = if (tunnelName == tunnel.name) when (handshakeStatus) {
|
||||
|
||||
+35
-34
@@ -96,8 +96,6 @@ fun SettingsScreen(
|
||||
val settings by viewModel.settings.collectAsStateWithLifecycle()
|
||||
val trustedSSIDs by viewModel.trustedSSIDs.collectAsStateWithLifecycle()
|
||||
val tunnels by viewModel.tunnels.collectAsStateWithLifecycle(mutableListOf())
|
||||
val backgroundLocationState =
|
||||
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
val fineLocationState = rememberPermissionState(Manifest.permission.ACCESS_FINE_LOCATION)
|
||||
var currentText by remember { mutableStateOf("") }
|
||||
val scrollState = rememberScrollState()
|
||||
@@ -135,41 +133,44 @@ fun SettingsScreen(
|
||||
context.startActivity(intentSettings)
|
||||
}
|
||||
}
|
||||
|
||||
if(!backgroundLocationState.status.isGranted && Build.VERSION.SDK_INT > Build.VERSION_CODES.P) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(padding)) {
|
||||
Icon(Icons.Rounded.LocationOff, contentDescription = stringResource(id = R.string.map), modifier = Modifier
|
||||
.padding(30.dp)
|
||||
.size(128.dp))
|
||||
Text(stringResource(R.string.prominent_background_location_title), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 20.sp)
|
||||
Text(stringResource(R.string.prominent_background_location_message), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp)
|
||||
Row(
|
||||
modifier = if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp) else Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(30.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Button(onClick = {
|
||||
navController.navigate(Routes.Main.name)
|
||||
}) {
|
||||
Text(stringResource(id = R.string.no_thanks))
|
||||
}
|
||||
Button(modifier = Modifier.focusRequester(focusRequester), onClick = {
|
||||
openSettings()
|
||||
}) {
|
||||
Text(stringResource(id = R.string.turn_on))
|
||||
if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val backgroundLocationState =
|
||||
rememberPermissionState(Manifest.permission.ACCESS_BACKGROUND_LOCATION)
|
||||
if(!backgroundLocationState.status.isGranted) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Top,
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(scrollState)
|
||||
.padding(padding)) {
|
||||
Icon(Icons.Rounded.LocationOff, contentDescription = stringResource(id = R.string.map), modifier = Modifier
|
||||
.padding(30.dp)
|
||||
.size(128.dp))
|
||||
Text(stringResource(R.string.prominent_background_location_title), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 20.sp)
|
||||
Text(stringResource(R.string.prominent_background_location_message), textAlign = TextAlign.Center, modifier = Modifier.padding(30.dp), fontSize = 15.sp)
|
||||
Row(
|
||||
modifier = if(WireGuardAutoTunnel.isRunningOnAndroidTv(context)) Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(10.dp) else Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(30.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Button(onClick = {
|
||||
navController.navigate(Routes.Main.name)
|
||||
}) {
|
||||
Text(stringResource(id = R.string.no_thanks))
|
||||
}
|
||||
Button(modifier = Modifier.focusRequester(focusRequester), onClick = {
|
||||
openSettings()
|
||||
}) {
|
||||
Text(stringResource(id = R.string.turn_on))
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
if(!fineLocationState.status.isGranted) {
|
||||
|
||||
+5
-5
@@ -1,20 +1,20 @@
|
||||
// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
|
||||
buildscript {
|
||||
val objectBoxVersion by extra("3.5.1")
|
||||
val hiltVersion by extra("2.47")
|
||||
val objectBoxVersion by extra("3.7.0")
|
||||
val hiltVersion by extra("2.48")
|
||||
val accompanistVersion by extra("0.31.2-alpha")
|
||||
|
||||
dependencies {
|
||||
classpath("io.objectbox:objectbox-gradle-plugin:$objectBoxVersion")
|
||||
classpath("com.google.gms:google-services:4.3.15")
|
||||
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.7")
|
||||
classpath("com.google.firebase:firebase-crashlytics-gradle:2.9.9")
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
id("com.android.application") version "8.2.0-beta01" apply false
|
||||
id("com.android.application") version "8.2.0-beta03" apply false
|
||||
id("org.jetbrains.kotlin.android") version "1.8.22" apply false
|
||||
id("com.google.dagger.hilt.android") version "2.44" apply false
|
||||
id("com.google.dagger.hilt.android") version "2.48" apply false
|
||||
kotlin("plugin.serialization") version "1.8.22" apply false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user