mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-22 11:11:02 +00:00
Rewrite WifiPermissionsActivity to Compose (#640)
This commit is contained in:
parent
06b4cf9477
commit
75a0c77b5f
|
@ -157,7 +157,7 @@
|
|||
android:parentActivityName=".ui.account.AccountActivity2" />
|
||||
<activity
|
||||
android:name=".ui.account.WifiPermissionsActivity"
|
||||
android:label="@string/wifi_permissions_label"
|
||||
android:theme="@style/AppTheme.NoActionBar"
|
||||
android:parentActivityName=".ui.account.SettingsActivity" />
|
||||
|
||||
<activity
|
||||
|
|
|
@ -16,6 +16,7 @@ object Constants {
|
|||
val HOMEPAGE_URL = "https://www.davx5.com".toUri()
|
||||
const val HOMEPAGE_PATH_FAQ = "faq"
|
||||
const val HOMEPAGE_PATH_FAQ_SYNC_NOT_RUN = "synchronization-is-not-run-as-expected"
|
||||
const val HOMEPAGE_PATH_FAQ_LOCATION_PERMISSION = "wifi-ssid-restriction-location-permission"
|
||||
const val HOMEPAGE_PATH_OPEN_SOURCE = "donate"
|
||||
const val HOMEPAGE_PATH_PRIVACY = "privacy"
|
||||
const val HOMEPAGE_PATH_TESTED_SERVICES = "tested-with"
|
||||
|
|
|
@ -16,26 +16,21 @@ import androidx.activity.viewModels
|
|||
import androidx.annotation.MainThread
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedButton
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
|
@ -45,10 +40,9 @@ import at.bitfire.davdroid.PackageChangedReceiver
|
|||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.ui.widget.CardWithImage
|
||||
import at.bitfire.davdroid.ui.widget.PermissionSwitchRow
|
||||
import at.bitfire.davdroid.util.PermissionUtils
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
import java.util.logging.Level
|
||||
|
||||
class PermissionsActivity: AppCompatActivity() {
|
||||
|
@ -149,82 +143,6 @@ fun PermissionsContent(model: PermissionsActivity.Model = viewModel()) {
|
|||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
fun PermissionSwitchRow(
|
||||
text: String,
|
||||
permissions: List<String>,
|
||||
summaryWhenGranted: String,
|
||||
summaryWhenNotGranted: String,
|
||||
modifier: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Normal
|
||||
) {
|
||||
val state = rememberMultiplePermissionsState(permissions = permissions.toList())
|
||||
|
||||
PermissionSwitchRow(
|
||||
text = text,
|
||||
fontWeight = fontWeight,
|
||||
summaryWhenGranted = summaryWhenGranted,
|
||||
summaryWhenNotGranted = summaryWhenNotGranted,
|
||||
allPermissionsGranted = state.allPermissionsGranted,
|
||||
onLaunchRequest = state::launchMultiplePermissionRequest,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PermissionSwitchRow_Preview() {
|
||||
PermissionSwitchRow(
|
||||
text = "Contacts",
|
||||
allPermissionsGranted = false,
|
||||
summaryWhenGranted = "Granted",
|
||||
summaryWhenNotGranted = "Not granted",
|
||||
onLaunchRequest = {}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PermissionSwitchRow(
|
||||
text: String,
|
||||
allPermissionsGranted: Boolean,
|
||||
summaryWhenGranted: String,
|
||||
summaryWhenNotGranted: String,
|
||||
modifier: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
onLaunchRequest: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
fontWeight = fontWeight,
|
||||
style = MaterialTheme.typography.body1
|
||||
)
|
||||
Text(
|
||||
text = if (allPermissionsGranted) summaryWhenGranted else summaryWhenNotGranted,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = allPermissionsGranted,
|
||||
enabled = !allPermissionsGranted,
|
||||
onCheckedChange = { checked ->
|
||||
if (checked) {
|
||||
onLaunchRequest()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun PermissionsCard(
|
||||
keepPermissions: Boolean?,
|
||||
|
|
|
@ -11,26 +11,58 @@ import android.content.BroadcastReceiver
|
|||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.IntentFilter
|
||||
import android.content.pm.PackageManager
|
||||
import android.location.LocationManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.activity.viewModels
|
||||
import androidx.annotation.MainThread
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.app.ActivityCompat
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.Divider
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.IconButton
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.OutlinedButton
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.TopAppBar
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.filled.ArrowBack
|
||||
import androidx.compose.material.icons.filled.CloudOff
|
||||
import androidx.compose.material.icons.filled.Help
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.app.TaskStackBuilder
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.getSystemService
|
||||
import androidx.core.location.LocationManagerCompat
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import at.bitfire.davdroid.Constants
|
||||
import at.bitfire.davdroid.Constants.withStatParams
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.databinding.ActivityWifiPermissionsBinding
|
||||
import at.bitfire.davdroid.log.Logger
|
||||
import at.bitfire.davdroid.ui.AppTheme
|
||||
import at.bitfire.davdroid.ui.widget.PermissionSwitchRow
|
||||
import at.bitfire.davdroid.util.PermissionUtils
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class WifiPermissionsActivity: AppCompatActivity() {
|
||||
|
||||
companion object {
|
||||
|
@ -44,48 +76,43 @@ class WifiPermissionsActivity: AppCompatActivity() {
|
|||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (!PermissionUtils.WIFI_SSID_PERMISSIONS.all { perm -> PermissionUtils.declaresPermission(packageManager, perm) })
|
||||
throw IllegalArgumentException("WiFi SSID restriction requires location permissions")
|
||||
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
|
||||
val binding = ActivityWifiPermissionsBinding.inflate(layoutInflater)
|
||||
binding.lifecycleOwner = this
|
||||
binding.model = model
|
||||
setContentView(binding.root)
|
||||
|
||||
model.needLocation.observe(this) { needPermission ->
|
||||
if (needPermission && model.haveLocation.value == false)
|
||||
ActivityCompat.requestPermissions(this, arrayOf(model.PERMISSION_LOCATION), 0)
|
||||
}
|
||||
|
||||
model.haveBackgroundLocation.observe(this) { status ->
|
||||
val label = if (Build.VERSION.SDK_INT >= 30)
|
||||
packageManager.backgroundPermissionOptionLabel
|
||||
else
|
||||
getString(R.string.wifi_permissions_background_location_permission_label)
|
||||
binding.backgroundLocationStatus.text = HtmlCompat.fromHtml(getString(
|
||||
if (status) R.string.wifi_permissions_background_location_permission_on else R.string.wifi_permissions_background_location_permission_off,
|
||||
label
|
||||
), HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
}
|
||||
model.needBackgroundLocation.observe(this) { needPermission ->
|
||||
if (needPermission && model.haveBackgroundLocation.value == false)
|
||||
ActivityCompat.requestPermissions(this, arrayOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION), 0)
|
||||
}
|
||||
binding.backgroundLocationDisclaimer.text = getString(R.string.wifi_permissions_background_location_disclaimer, getString(R.string.app_name))
|
||||
|
||||
binding.settingsBtn.setOnClickListener {
|
||||
PermissionUtils.showAppSettings(this)
|
||||
}
|
||||
|
||||
model.needLocationEnabled.observe(this) { needLocation ->
|
||||
if (needLocation != null && needLocation != model.isLocationEnabled.value) {
|
||||
val intent = Intent(Settings.ACTION_LOCATION_SOURCE_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
else
|
||||
Logger.log.warning("Couldn't resolve Location settings Intent")
|
||||
setContent {
|
||||
AppTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBar(
|
||||
navigationIcon = {
|
||||
IconButton(onClick = { onSupportNavigateUp() }) {
|
||||
Icon(Icons.AutoMirrored.Default.ArrowBack, stringResource(R.string.navigate_up))
|
||||
}
|
||||
},
|
||||
title = { Text(stringResource(R.string.wifi_permissions_label)) },
|
||||
actions = {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
IconButton(onClick = {
|
||||
uriHandler.openUri(Constants.HOMEPAGE_URL.buildUpon()
|
||||
.appendPath(Constants.HOMEPAGE_PATH_FAQ)
|
||||
.appendPath(Constants.HOMEPAGE_PATH_FAQ_LOCATION_PERMISSION)
|
||||
.withStatParams("WifiPermissionsActivity")
|
||||
.build().toString())
|
||||
}) {
|
||||
Icon(Icons.Default.Help, stringResource(R.string.help))
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
) { padding ->
|
||||
Box(modifier = Modifier.padding(padding)) {
|
||||
Content(
|
||||
backgroundPermissionOptionLabel =
|
||||
if (Build.VERSION.SDK_INT >= 30)
|
||||
packageManager.backgroundPermissionOptionLabel.toString()
|
||||
else
|
||||
stringResource(R.string.wifi_permissions_background_location_permission_label),
|
||||
locationServiceEnabled = model.isLocationEnabled.observeAsState(false).value
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -96,70 +123,169 @@ class WifiPermissionsActivity: AppCompatActivity() {
|
|||
builder.editIntentAt(builder.intentCount - 1)?.putExtra(SettingsActivity.EXTRA_ACCOUNT, account)
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
model.checkPermissions()
|
||||
}
|
||||
|
||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
|
||||
model.checkPermissions()
|
||||
}
|
||||
@Composable
|
||||
fun Content(
|
||||
backgroundPermissionOptionLabel: String,
|
||||
locationServiceEnabled: Boolean
|
||||
) {
|
||||
Column(Modifier
|
||||
.padding(8.dp)
|
||||
.verticalScroll(rememberScrollState())) {
|
||||
Text(
|
||||
stringResource(R.string.wifi_permissions_intro),
|
||||
style = MaterialTheme.typography.body1
|
||||
)
|
||||
|
||||
|
||||
class Model(app: Application): AndroidViewModel(app) {
|
||||
|
||||
val PERMISSION_LOCATION =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
Manifest.permission.ACCESS_FINE_LOCATION // since Android 10, fine location is required
|
||||
else
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION // Android 8+: coarse location is enough
|
||||
|
||||
val haveLocation = MutableLiveData<Boolean>()
|
||||
val needLocation = MutableLiveData<Boolean>()
|
||||
|
||||
val haveBackgroundLocation = MutableLiveData<Boolean>()
|
||||
val needBackgroundLocation = MutableLiveData<Boolean>()
|
||||
|
||||
val isLocationEnabled = MutableLiveData<Boolean>()
|
||||
val needLocationEnabled = MutableLiveData<Boolean>()
|
||||
val locationModeWatcher = object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
checkPermissions()
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
app.registerReceiver(locationModeWatcher, IntentFilter(LocationManager.MODE_CHANGED_ACTION))
|
||||
checkPermissions()
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
getApplication<Application>().unregisterReceiver(locationModeWatcher)
|
||||
}
|
||||
|
||||
fun checkPermissions() {
|
||||
// Android 8.1+: location permission
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) {
|
||||
val location = ContextCompat.checkSelfPermission(getApplication(), PERMISSION_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
haveLocation.value = location
|
||||
needLocation.value = location
|
||||
}
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1)
|
||||
LocationPermission(
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
// Android 10+: background location permission
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
BackgroundLocationPermission(
|
||||
backgroundPermissionOptionLabel = backgroundPermissionOptionLabel,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
// Android 9+: location service
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
||||
getApplication<Application>().getSystemService<LocationManager>()?.let { locationManager ->
|
||||
val locationEnabled = LocationManagerCompat.isLocationEnabled(locationManager)
|
||||
isLocationEnabled.value = locationEnabled
|
||||
needLocationEnabled.value = locationEnabled
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P)
|
||||
LocationService(
|
||||
locationServiceEnabled = locationServiceEnabled,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
|
||||
// If permissions have actively been denied
|
||||
Text(
|
||||
stringResource(R.string.permissions_app_settings_hint),
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier.padding(top = 16.dp)
|
||||
)
|
||||
val context = LocalContext.current
|
||||
OutlinedButton(
|
||||
onClick = { PermissionUtils.showAppSettings(context) }
|
||||
) {
|
||||
Text(stringResource(R.string.permissions_app_settings).uppercase())
|
||||
}
|
||||
|
||||
Divider(Modifier.padding(vertical = 16.dp))
|
||||
|
||||
// Disclaimer
|
||||
Row {
|
||||
Text(
|
||||
stringResource(R.string.wifi_permissions_background_location_disclaimer, stringResource(R.string.app_name)),
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
Icon(Icons.Default.CloudOff, null, modifier = Modifier.padding(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LocationPermission(
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val permission = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
|
||||
Manifest.permission.ACCESS_FINE_LOCATION // since Android 10, fine location is required
|
||||
else
|
||||
Manifest.permission.ACCESS_COARSE_LOCATION // Android 8+: coarse location is enough
|
||||
|
||||
PermissionSwitchRow(
|
||||
text = stringResource(R.string.wifi_permissions_location_permission),
|
||||
permissions = listOf(permission),
|
||||
summaryWhenGranted = stringResource(R.string.wifi_permissions_location_permission_on),
|
||||
summaryWhenNotGranted = stringResource(R.string.wifi_permissions_location_permission_off),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
@Composable
|
||||
fun BackgroundLocationPermission(
|
||||
backgroundPermissionOptionLabel: String,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
PermissionSwitchRow(
|
||||
text = stringResource(R.string.wifi_permissions_background_location_permission),
|
||||
permissions = listOf(Manifest.permission.ACCESS_BACKGROUND_LOCATION),
|
||||
summaryWhenGranted = stringResource(R.string.wifi_permissions_background_location_permission_on, backgroundPermissionOptionLabel),
|
||||
summaryWhenNotGranted = stringResource(R.string.wifi_permissions_background_location_permission_off, backgroundPermissionOptionLabel),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun LocationService(
|
||||
locationServiceEnabled: Boolean,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
Row(modifier.fillMaxWidth()) {
|
||||
Column(Modifier.weight(1f)) {
|
||||
Text(
|
||||
stringResource(R.string.wifi_permissions_location_enabled),
|
||||
style = MaterialTheme.typography.body1
|
||||
)
|
||||
Text(
|
||||
stringResource(
|
||||
if (locationServiceEnabled)
|
||||
R.string.wifi_permissions_location_enabled_on
|
||||
else
|
||||
R.string.wifi_permissions_location_enabled_off
|
||||
),
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = locationServiceEnabled,
|
||||
onCheckedChange = {
|
||||
val intent = Intent(android.provider.Settings.ACTION_LOCATION_SOURCE_SETTINGS)
|
||||
if (intent.resolveActivity(packageManager) != null)
|
||||
startActivity(intent)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@Preview
|
||||
fun Content_Preview() {
|
||||
Content(
|
||||
backgroundPermissionOptionLabel = stringResource(R.string.wifi_permissions_background_location_permission_label),
|
||||
locationServiceEnabled = true
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class Model @Inject constructor(
|
||||
context: Application
|
||||
): ViewModel() {
|
||||
|
||||
val isLocationEnabled: LiveData<Boolean> = object: LiveData<Boolean>() {
|
||||
val locationChangedReceiver = object: BroadcastReceiver() {
|
||||
override fun onReceive(context: Context, intent: Intent) {
|
||||
update()
|
||||
}
|
||||
}
|
||||
|
||||
// Android 10+: background location permission
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
|
||||
val backgroundLocation = ContextCompat.checkSelfPermission(getApplication(), Manifest.permission.ACCESS_BACKGROUND_LOCATION) == PackageManager.PERMISSION_GRANTED
|
||||
haveBackgroundLocation.value = backgroundLocation
|
||||
needBackgroundLocation.value = backgroundLocation
|
||||
override fun onActive() {
|
||||
context.registerReceiver(locationChangedReceiver, IntentFilter(LocationManager.MODE_CHANGED_ACTION))
|
||||
update()
|
||||
}
|
||||
|
||||
override fun onInactive() {
|
||||
context.unregisterReceiver(locationChangedReceiver)
|
||||
}
|
||||
|
||||
@MainThread
|
||||
fun update() {
|
||||
context.getSystemService<LocationManager>()?.let { locationManager ->
|
||||
val locationEnabled = LocationManagerCompat.isLocationEnabled(locationManager)
|
||||
value = locationEnabled
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
package at.bitfire.davdroid.ui.widget
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalInspectionMode
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.google.accompanist.permissions.ExperimentalPermissionsApi
|
||||
import com.google.accompanist.permissions.rememberMultiplePermissionsState
|
||||
|
||||
@Composable
|
||||
fun PermissionSwitchRow(
|
||||
text: String,
|
||||
allPermissionsGranted: Boolean,
|
||||
summaryWhenGranted: String,
|
||||
summaryWhenNotGranted: String,
|
||||
modifier: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Normal,
|
||||
onLaunchRequest: () -> Unit
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier.weight(1f)
|
||||
) {
|
||||
Text(
|
||||
text = text,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
fontWeight = fontWeight,
|
||||
style = MaterialTheme.typography.body1
|
||||
)
|
||||
Text(
|
||||
text = if (allPermissionsGranted) summaryWhenGranted else summaryWhenNotGranted,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
Switch(
|
||||
checked = allPermissionsGranted,
|
||||
enabled = !allPermissionsGranted,
|
||||
onCheckedChange = { checked ->
|
||||
if (checked) {
|
||||
onLaunchRequest()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@OptIn(ExperimentalPermissionsApi::class)
|
||||
fun PermissionSwitchRow(
|
||||
text: String,
|
||||
permissions: List<String>,
|
||||
summaryWhenGranted: String,
|
||||
summaryWhenNotGranted: String,
|
||||
modifier: Modifier = Modifier,
|
||||
fontWeight: FontWeight = FontWeight.Normal
|
||||
) {
|
||||
if (LocalInspectionMode.current) {
|
||||
// preview
|
||||
PermissionSwitchRow(
|
||||
text = text,
|
||||
fontWeight = fontWeight,
|
||||
summaryWhenGranted = summaryWhenGranted,
|
||||
summaryWhenNotGranted = summaryWhenNotGranted,
|
||||
allPermissionsGranted = false,
|
||||
onLaunchRequest = {},
|
||||
modifier = modifier
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
val state = rememberMultiplePermissionsState(permissions = permissions.toList())
|
||||
PermissionSwitchRow(
|
||||
text = text,
|
||||
fontWeight = fontWeight,
|
||||
summaryWhenGranted = summaryWhenGranted,
|
||||
summaryWhenNotGranted = summaryWhenNotGranted,
|
||||
allPermissionsGranted = state.allPermissionsGranted,
|
||||
onLaunchRequest = state::launchMultiplePermissionRequest,
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PermissionSwitchRow_Preview_NotGranted() {
|
||||
PermissionSwitchRow(
|
||||
text = "Contacts",
|
||||
allPermissionsGranted = false,
|
||||
summaryWhenGranted = "Granted",
|
||||
summaryWhenNotGranted = "Not granted",
|
||||
onLaunchRequest = {}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
fun PermissionSwitchRow_Preview_Granted() {
|
||||
PermissionSwitchRow(
|
||||
text = "Contacts",
|
||||
allPermissionsGranted = true,
|
||||
summaryWhenGranted = "Granted",
|
||||
summaryWhenNotGranted = "Not granted",
|
||||
onLaunchRequest = {}
|
||||
)
|
||||
}
|
|
@ -1,176 +0,0 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
tools:context=".ui.account.WifiPermissionsActivity">
|
||||
|
||||
<data>
|
||||
<variable name="model" type="at.bitfire.davdroid.ui.account.WifiPermissionsActivity.Model" />
|
||||
<import type="android.view.View" />
|
||||
</data>
|
||||
|
||||
<ScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:padding="@dimen/activity_margin">
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_width="match_parent" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/intro"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/wifi_permissions_intro"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/locationTitle"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/wifi_permissions_location_permission"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.haveLocation != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/locationSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/intro" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/locationStatus"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.haveLocation ? @string/wifi_permissions_location_permission_on : @string/wifi_permissions_location_permission_off}"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.haveLocation != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/locationSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationTitle" />
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/locationSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="@id/locationTitle"
|
||||
app:layout_constraintBottom_toBottomOf="@id/locationStatus"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="@{model.haveLocation != null ? View.VISIBLE : View.GONE}"
|
||||
android:clickable="@{!model.haveLocation}"
|
||||
android:checked="@={model.needLocation}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/backgroundLocationTitle"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="@dimen/card_margin_title_text"
|
||||
android:text="@string/wifi_permissions_background_location_permission"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.haveBackgroundLocation != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/backgroundLocationSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationStatus" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/backgroundLocationStatus"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.haveBackgroundLocation != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/backgroundLocationSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backgroundLocationTitle"
|
||||
tools:text="@string/wifi_permissions_background_location_permission_off" />
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/backgroundLocationSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="@id/backgroundLocationTitle"
|
||||
app:layout_constraintBottom_toBottomOf="@id/backgroundLocationStatus"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="@{model.haveBackgroundLocation != null ? View.VISIBLE : View.GONE}"
|
||||
android:enabled="@{model.haveLocation}"
|
||||
android:clickable="@{!model.haveBackgroundLocation}"
|
||||
android:checked="@={model.needBackgroundLocation}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/backgroundLocationDisclaimer"
|
||||
app:drawableStartCompat="@drawable/ic_info"
|
||||
android:drawablePadding="8dp"
|
||||
style="@style/TextAppearance.MaterialComponents.Caption"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/wifi_permissions_background_location_disclaimer"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.haveBackgroundLocation != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backgroundLocationStatus" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/locationEnabledTitle"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/wifi_permissions_location_enabled"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.isLocationEnabled != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/locationEnabledSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/backgroundLocationDisclaimer" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/locationEnabledStatus"
|
||||
style="@style/TextAppearance.MaterialComponents.Body2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@{model.isLocationEnabled ? @string/wifi_permissions_location_enabled_on : @string/wifi_permissions_location_enabled_off}"
|
||||
android:textAlignment="viewStart"
|
||||
android:visibility="@{model.isLocationEnabled != null ? View.VISIBLE : View.GONE}"
|
||||
app:layout_constraintEnd_toStartOf="@id/locationEnabledSwitch"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationEnabledTitle" />
|
||||
<com.google.android.material.switchmaterial.SwitchMaterial
|
||||
android:id="@+id/locationEnabledSwitch"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_constraintTop_toTopOf="@id/locationEnabledTitle"
|
||||
app:layout_constraintBottom_toBottomOf="@id/locationEnabledStatus"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
android:visibility="@{model.isLocationEnabled != null ? View.VISIBLE : View.GONE}"
|
||||
android:checked="@={model.needLocationEnabled}" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/settingsHint"
|
||||
style="@style/TextAppearance.MaterialComponents.Body1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp"
|
||||
android:text="@string/permissions_app_settings_hint"
|
||||
android:textAlignment="viewStart"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toBottomOf="@id/locationEnabledStatus" />
|
||||
|
||||
<Button
|
||||
android:id="@+id/settingsBtn"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Widget.MaterialComponents.Button.OutlinedButton"
|
||||
app:layout_constraintTop_toBottomOf="@id/settingsHint"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
android:layout_marginTop="8dp"
|
||||
android:text="@string/permissions_app_settings"/>
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
</ScrollView>
|
||||
|
||||
</layout>
|
|
@ -99,8 +99,8 @@
|
|||
<string name="wifi_permissions_location_permission_off">Location permission denied</string>
|
||||
<string name="wifi_permissions_background_location_permission">Background location permission</string>
|
||||
<string name="wifi_permissions_background_location_permission_label">Allow all the time</string>
|
||||
<string name="wifi_permissions_background_location_permission_on"><![CDATA[Location permission set to <b>%s</b>]]></string>
|
||||
<string name="wifi_permissions_background_location_permission_off"><![CDATA[Location permission not set to <b>%s</b>]]></string>
|
||||
<string name="wifi_permissions_background_location_permission_on">Location permission set to: %s</string>
|
||||
<string name="wifi_permissions_background_location_permission_off">Location permission not set to: %s</string>
|
||||
<string name="wifi_permissions_background_location_disclaimer">%s uses the Location permission only to determine the current WiFi\'s SSID for SSID-restricted accounts. This will happen even when the app is in background. No location data are collected, stored, processed or sent anywhere.</string>
|
||||
<string name="wifi_permissions_location_enabled">Location always enabled</string>
|
||||
<string name="wifi_permissions_location_enabled_on">Location service is enabled</string>
|
||||
|
|
Loading…
Reference in a new issue