mirror of
https://github.com/bitfireAT/davx5-ose
synced 2024-07-22 03:01:24 +00:00
Rewrite TasksIntroPage to M3 (#760)
* Migrated to M3 Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Fixed theme Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Moved Composables and Model to individual files Signed-off-by: Arnau Mora <arnyminerz@proton.me> * Move AppTheme to screen composable * Fixed model name Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> * Minor re-ordering --------- Signed-off-by: Arnau Mora <arnyminerz@proton.me> Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me> Co-authored-by: Sunik Kupfer <kupfer@bitfire.at> Co-authored-by: Ricki Hirner <hirner@bitfire.at>
This commit is contained in:
parent
857309c451
commit
364f372a8b
|
@ -79,11 +79,11 @@ import at.bitfire.davdroid.util.broadcastReceiverFlow
|
|||
import at.bitfire.ical4android.TaskProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class AppSettingsActivity: AppCompatActivity() {
|
||||
|
@ -541,7 +541,7 @@ class AppSettingsActivity: AppCompatActivity() {
|
|||
fun resetHints() {
|
||||
settings.remove(BatteryOptimizationsPage.Model.HINT_BATTERY_OPTIMIZATIONS)
|
||||
settings.remove(BatteryOptimizationsPage.Model.HINT_AUTOSTART_PERMISSION)
|
||||
settings.remove(TasksActivity.Model.HINT_OPENTASKS_NOT_INSTALLED)
|
||||
settings.remove(TasksModel.HINT_OPENTASKS_NOT_INSTALLED)
|
||||
settings.remove(OpenSourcePage.Model.SETTING_NEXT_DONATION_POPUP)
|
||||
}
|
||||
|
||||
|
|
|
@ -4,64 +4,10 @@
|
|||
|
||||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.compose.setContent
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.Checkbox
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Scaffold
|
||||
import androidx.compose.material.SnackbarDuration
|
||||
import androidx.compose.material.SnackbarHostState
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAlignment
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
|
||||
import at.bitfire.davdroid.ui.composable.BasicTopAppBar
|
||||
import at.bitfire.davdroid.ui.composable.CardWithImage
|
||||
import at.bitfire.davdroid.ui.composable.RadioWithSwitch
|
||||
import at.bitfire.davdroid.ui.widget.ClickableTextWithLink
|
||||
import at.bitfire.davdroid.util.TaskUtils
|
||||
import at.bitfire.davdroid.util.packageChangedFlow
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TasksActivity: AppCompatActivity() {
|
||||
|
@ -70,263 +16,9 @@ class TasksActivity: AppCompatActivity() {
|
|||
super.onCreate(savedInstanceState)
|
||||
|
||||
setContent {
|
||||
M2Theme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
BasicTopAppBar(
|
||||
titleStringRes = R.string.intro_tasks_title,
|
||||
onNavigateUp = ::onSupportNavigateUp
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Box(Modifier.padding(paddingValues)) {
|
||||
TasksCard()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@HiltViewModel
|
||||
class Model @Inject constructor(
|
||||
val context: Application,
|
||||
val settings: SettingsManager
|
||||
) : ViewModel() {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Whether this fragment (which asks for OpenTasks installation) shall be shown.
|
||||
* If this setting is true or null/not set, the notice shall be shown. Only if this
|
||||
* setting is false, the notice shall not be shown.
|
||||
*/
|
||||
const val HINT_OPENTASKS_NOT_INSTALLED = "hint_OpenTasksNotInstalled"
|
||||
|
||||
}
|
||||
|
||||
val showAgain = settings.getBooleanFlow(HINT_OPENTASKS_NOT_INSTALLED, true)
|
||||
fun setShowAgain(showAgain: Boolean) {
|
||||
if (showAgain)
|
||||
settings.remove(HINT_OPENTASKS_NOT_INSTALLED)
|
||||
else
|
||||
settings.putBoolean(HINT_OPENTASKS_NOT_INSTALLED, false)
|
||||
}
|
||||
|
||||
val currentProvider = TaskUtils.currentProviderFlow(context, viewModelScope)
|
||||
val jtxSelected = currentProvider.map { it == TaskProvider.ProviderName.JtxBoard }
|
||||
val tasksOrgSelected = currentProvider.map { it == TaskProvider.ProviderName.TasksOrg }
|
||||
val openTasksSelected = currentProvider.map { it == TaskProvider.ProviderName.OpenTasks }
|
||||
|
||||
var jtxInstalled by mutableStateOf(false)
|
||||
var tasksOrgInstalled by mutableStateOf(false)
|
||||
var openTasksInstalled by mutableStateOf(false)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
packageChangedFlow(context).collect {
|
||||
jtxInstalled = isInstalled(TaskProvider.ProviderName.JtxBoard.packageName)
|
||||
tasksOrgInstalled = isInstalled(TaskProvider.ProviderName.TasksOrg.packageName)
|
||||
openTasksInstalled = isInstalled(TaskProvider.ProviderName.OpenTasks.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInstalled(packageName: String): Boolean =
|
||||
try {
|
||||
context.packageManager.getPackageInfo(packageName, 0)
|
||||
true
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
|
||||
fun selectProvider(provider: TaskProvider.ProviderName) = viewModelScope.launch(Dispatchers.Default) {
|
||||
TaskUtils.selectProvider(context, provider)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun TasksCard(
|
||||
model: TasksActivity.Model = viewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
val jtxInstalled = model.jtxInstalled
|
||||
val jtxSelected by model.jtxSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val tasksOrgInstalled = model.tasksOrgInstalled
|
||||
val tasksOrgSelected by model.tasksOrgSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val openTasksInstalled = model.openTasksInstalled
|
||||
val openTasksSelected by model.openTasksSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val showAgain by model.showAgain.collectAsStateWithLifecycle(true)
|
||||
|
||||
TasksCardContent(
|
||||
jtxSelected = jtxSelected,
|
||||
jtxInstalled = jtxInstalled,
|
||||
tasksOrgSelected = tasksOrgSelected,
|
||||
tasksOrgInstalled = tasksOrgInstalled,
|
||||
openTasksSelected = openTasksSelected,
|
||||
openTasksInstalled = openTasksInstalled,
|
||||
showAgain = showAgain,
|
||||
onSetShowAgain = model::setShowAgain,
|
||||
onProviderSelected = { provider ->
|
||||
if (model.currentProvider.value != provider)
|
||||
model.selectProvider(provider)
|
||||
},
|
||||
installApp = { packageName ->
|
||||
val uri = Uri.parse("market://details?id=$packageName&referrer=" +
|
||||
Uri.encode("utm_source=" + BuildConfig.APPLICATION_ID))
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
if (intent.resolveActivity(context.packageManager) != null)
|
||||
context.startActivity(intent)
|
||||
else
|
||||
coroutineScope.launch {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = context.getString(R.string.intro_tasks_no_app_store),
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, showSystemUi = true)
|
||||
@Composable
|
||||
fun TasksCardContent_Preview() {
|
||||
M2Theme {
|
||||
TasksCardContent(
|
||||
jtxSelected = true,
|
||||
jtxInstalled = true,
|
||||
tasksOrgSelected = false,
|
||||
tasksOrgInstalled = false,
|
||||
openTasksSelected = false,
|
||||
openTasksInstalled = false,
|
||||
showAgain = true,
|
||||
onSetShowAgain = {},
|
||||
onProviderSelected = {},
|
||||
installApp = {}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TasksCardContent(
|
||||
jtxSelected: Boolean,
|
||||
jtxInstalled: Boolean,
|
||||
tasksOrgSelected: Boolean,
|
||||
tasksOrgInstalled: Boolean,
|
||||
openTasksSelected: Boolean,
|
||||
openTasksInstalled: Boolean,
|
||||
showAgain: Boolean,
|
||||
onSetShowAgain: (Boolean) -> Unit,
|
||||
onProviderSelected: (TaskProvider.ProviderName) -> Unit,
|
||||
installApp: (String) -> Unit,
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
CardWithImage(
|
||||
image = painterResource(R.drawable.intro_tasks),
|
||||
imageAlignment = BiasAlignment(0f, .1f),
|
||||
title = stringResource(R.string.intro_tasks_title),
|
||||
message = stringResource(R.string.intro_tasks_text1),
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_jtx),
|
||||
summary = {
|
||||
Text(stringResource(R.string.intro_tasks_jtx_info))
|
||||
},
|
||||
isSelected = jtxSelected,
|
||||
isToggled = jtxInstalled,
|
||||
enabled = jtxInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.JtxBoard) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.JtxBoard.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
TasksScreen(
|
||||
onNavUp = ::onSupportNavigateUp
|
||||
)
|
||||
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_tasks_org),
|
||||
summary = {
|
||||
val summary = HtmlCompat.fromHtml(
|
||||
stringResource(R.string.intro_tasks_tasks_org_info),
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
).toAnnotatedString()
|
||||
ClickableTextWithLink(summary)
|
||||
},
|
||||
isSelected = tasksOrgSelected,
|
||||
isToggled = tasksOrgInstalled,
|
||||
enabled = tasksOrgInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.TasksOrg) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.TasksOrg.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_opentasks),
|
||||
summary = {
|
||||
Text(stringResource(R.string.intro_tasks_opentasks_info))
|
||||
},
|
||||
isSelected = openTasksSelected,
|
||||
isToggled = openTasksInstalled,
|
||||
enabled = openTasksInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.OpenTasks) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.OpenTasks.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
) {
|
||||
Checkbox(
|
||||
checked = !showAgain,
|
||||
onCheckedChange = { onSetShowAgain(!it) }
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.intro_tasks_dont_show),
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onSetShowAgain(!showAgain) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.intro_leave_unchecked,
|
||||
stringResource(R.string.app_settings_reset_hints)
|
||||
),
|
||||
style = MaterialTheme.typography.body2,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
76
app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt
Normal file
76
app/src/main/kotlin/at/bitfire/davdroid/ui/TasksModel.kt
Normal file
|
@ -0,0 +1,76 @@
|
|||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.app.Application
|
||||
import android.content.pm.PackageManager
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import at.bitfire.davdroid.util.TaskUtils
|
||||
import at.bitfire.davdroid.util.packageChangedFlow
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@HiltViewModel
|
||||
class TasksModel @Inject constructor(
|
||||
val context: Application,
|
||||
val settings: SettingsManager
|
||||
) : ViewModel() {
|
||||
|
||||
companion object {
|
||||
|
||||
/**
|
||||
* Whether this fragment (which asks for OpenTasks installation) shall be shown.
|
||||
* If this setting is true or null/not set, the notice shall be shown. Only if this
|
||||
* setting is false, the notice shall not be shown.
|
||||
*/
|
||||
const val HINT_OPENTASKS_NOT_INSTALLED = "hint_OpenTasksNotInstalled"
|
||||
|
||||
}
|
||||
|
||||
val showAgain = settings.getBooleanFlow(HINT_OPENTASKS_NOT_INSTALLED, true)
|
||||
fun setShowAgain(showAgain: Boolean) {
|
||||
if (showAgain)
|
||||
settings.remove(HINT_OPENTASKS_NOT_INSTALLED)
|
||||
else
|
||||
settings.putBoolean(HINT_OPENTASKS_NOT_INSTALLED, false)
|
||||
}
|
||||
|
||||
val currentProvider = TaskUtils.currentProviderFlow(context, viewModelScope)
|
||||
val jtxSelected = currentProvider.map { it == TaskProvider.ProviderName.JtxBoard }
|
||||
val tasksOrgSelected = currentProvider.map { it == TaskProvider.ProviderName.TasksOrg }
|
||||
val openTasksSelected = currentProvider.map { it == TaskProvider.ProviderName.OpenTasks }
|
||||
|
||||
var jtxInstalled by mutableStateOf(false)
|
||||
var tasksOrgInstalled by mutableStateOf(false)
|
||||
var openTasksInstalled by mutableStateOf(false)
|
||||
|
||||
init {
|
||||
viewModelScope.launch {
|
||||
packageChangedFlow(context).collect {
|
||||
jtxInstalled = isInstalled(TaskProvider.ProviderName.JtxBoard.packageName)
|
||||
tasksOrgInstalled = isInstalled(TaskProvider.ProviderName.TasksOrg.packageName)
|
||||
openTasksInstalled = isInstalled(TaskProvider.ProviderName.OpenTasks.packageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun isInstalled(packageName: String): Boolean =
|
||||
try {
|
||||
context.packageManager.getPackageInfo(packageName, 0)
|
||||
true
|
||||
} catch (e: PackageManager.NameNotFoundException) {
|
||||
false
|
||||
}
|
||||
|
||||
fun selectProvider(provider: TaskProvider.ProviderName) = viewModelScope.launch(Dispatchers.Default) {
|
||||
TaskUtils.selectProvider(context, provider)
|
||||
}
|
||||
|
||||
}
|
240
app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt
Normal file
240
app/src/main/kotlin/at/bitfire/davdroid/ui/TasksScreen.kt
Normal file
|
@ -0,0 +1,240 @@
|
|||
package at.bitfire.davdroid.ui
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
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.material3.Checkbox
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.material3.SnackbarDuration
|
||||
import androidx.compose.material3.SnackbarHostState
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.BiasAlignment
|
||||
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.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.text.HtmlCompat
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import at.bitfire.davdroid.BuildConfig
|
||||
import at.bitfire.davdroid.R
|
||||
import at.bitfire.davdroid.ui.UiUtils.toAnnotatedString
|
||||
import at.bitfire.davdroid.ui.composable.BasicTopAppBar
|
||||
import at.bitfire.davdroid.ui.composable.CardWithImage
|
||||
import at.bitfire.davdroid.ui.composable.RadioWithSwitch
|
||||
import at.bitfire.davdroid.ui.widget.ClickableTextWithLink
|
||||
import at.bitfire.ical4android.TaskProvider
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun TasksScreen(onNavUp: () -> Unit) {
|
||||
AppTheme {
|
||||
Scaffold(
|
||||
topBar = {
|
||||
BasicTopAppBar(
|
||||
titleStringRes = R.string.intro_tasks_title,
|
||||
onNavigateUp = onNavUp
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Box(Modifier.padding(paddingValues)) {
|
||||
TasksCard()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TasksCard(
|
||||
model: TasksModel = viewModel()
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
val snackbarHostState = remember { SnackbarHostState() }
|
||||
|
||||
val jtxInstalled = model.jtxInstalled
|
||||
val jtxSelected by model.jtxSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val tasksOrgInstalled = model.tasksOrgInstalled
|
||||
val tasksOrgSelected by model.tasksOrgSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val openTasksInstalled = model.openTasksInstalled
|
||||
val openTasksSelected by model.openTasksSelected.collectAsStateWithLifecycle(false)
|
||||
|
||||
val showAgain by model.showAgain.collectAsStateWithLifecycle(true)
|
||||
|
||||
TasksCard(
|
||||
jtxSelected = jtxSelected,
|
||||
jtxInstalled = jtxInstalled,
|
||||
tasksOrgSelected = tasksOrgSelected,
|
||||
tasksOrgInstalled = tasksOrgInstalled,
|
||||
openTasksSelected = openTasksSelected,
|
||||
openTasksInstalled = openTasksInstalled,
|
||||
showAgain = showAgain,
|
||||
onSetShowAgain = model::setShowAgain,
|
||||
onProviderSelected = { provider ->
|
||||
if (model.currentProvider.value != provider)
|
||||
model.selectProvider(provider)
|
||||
},
|
||||
installApp = { packageName ->
|
||||
val uri = Uri.parse("market://details?id=$packageName&referrer=" +
|
||||
Uri.encode("utm_source=" + BuildConfig.APPLICATION_ID))
|
||||
val intent = Intent(Intent.ACTION_VIEW, uri)
|
||||
if (intent.resolveActivity(context.packageManager) != null)
|
||||
context.startActivity(intent)
|
||||
else
|
||||
coroutineScope.launch {
|
||||
snackbarHostState.showSnackbar(
|
||||
message = context.getString(R.string.intro_tasks_no_app_store),
|
||||
duration = SnackbarDuration.Long
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun TasksCard(
|
||||
jtxSelected: Boolean,
|
||||
jtxInstalled: Boolean,
|
||||
tasksOrgSelected: Boolean,
|
||||
tasksOrgInstalled: Boolean,
|
||||
openTasksSelected: Boolean,
|
||||
openTasksInstalled: Boolean,
|
||||
onProviderSelected: (TaskProvider.ProviderName) -> Unit = {},
|
||||
installApp: (String) -> Unit = {},
|
||||
showAgain: Boolean,
|
||||
onSetShowAgain: (Boolean) -> Unit = {}
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxHeight()
|
||||
.verticalScroll(rememberScrollState())
|
||||
) {
|
||||
CardWithImage(
|
||||
image = painterResource(R.drawable.intro_tasks),
|
||||
imageAlignment = BiasAlignment(0f, .1f),
|
||||
title = stringResource(R.string.intro_tasks_title),
|
||||
message = stringResource(R.string.intro_tasks_text1),
|
||||
modifier = Modifier.padding(8.dp)
|
||||
) {
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_jtx),
|
||||
summary = {
|
||||
Text(stringResource(R.string.intro_tasks_jtx_info))
|
||||
},
|
||||
isSelected = jtxSelected,
|
||||
isToggled = jtxInstalled,
|
||||
enabled = jtxInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.JtxBoard) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.JtxBoard.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_tasks_org),
|
||||
summary = {
|
||||
val summary = HtmlCompat.fromHtml(
|
||||
stringResource(R.string.intro_tasks_tasks_org_info),
|
||||
HtmlCompat.FROM_HTML_MODE_COMPACT
|
||||
).toAnnotatedString()
|
||||
ClickableTextWithLink(summary)
|
||||
},
|
||||
isSelected = tasksOrgSelected,
|
||||
isToggled = tasksOrgInstalled,
|
||||
enabled = tasksOrgInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.TasksOrg) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.TasksOrg.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
|
||||
RadioWithSwitch(
|
||||
title = stringResource(R.string.intro_tasks_opentasks),
|
||||
summary = {
|
||||
Text(stringResource(R.string.intro_tasks_opentasks_info))
|
||||
},
|
||||
isSelected = openTasksSelected,
|
||||
isToggled = openTasksInstalled,
|
||||
enabled = openTasksInstalled,
|
||||
onSelected = { onProviderSelected(TaskProvider.ProviderName.OpenTasks) },
|
||||
onToggled = { toggled ->
|
||||
if (toggled) installApp(TaskProvider.ProviderName.OpenTasks.packageName)
|
||||
},
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 12.dp)
|
||||
) {
|
||||
Checkbox(
|
||||
checked = !showAgain,
|
||||
onCheckedChange = { onSetShowAgain(!it) }
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.intro_tasks_dont_show),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clickable { onSetShowAgain(!showAgain) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Text(
|
||||
text = stringResource(
|
||||
R.string.intro_leave_unchecked,
|
||||
stringResource(R.string.app_settings_reset_hints)
|
||||
),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true, showSystemUi = true)
|
||||
@Composable
|
||||
fun TasksCard_Preview() {
|
||||
AppTheme {
|
||||
TasksCard(
|
||||
jtxSelected = true,
|
||||
jtxInstalled = true,
|
||||
tasksOrgSelected = false,
|
||||
tasksOrgInstalled = false,
|
||||
openTasksSelected = false,
|
||||
openTasksInstalled = false,
|
||||
showAgain = true
|
||||
)
|
||||
}
|
||||
}
|
|
@ -7,8 +7,8 @@ package at.bitfire.davdroid.ui.intro
|
|||
import android.app.Application
|
||||
import androidx.compose.runtime.Composable
|
||||
import at.bitfire.davdroid.settings.SettingsManager
|
||||
import at.bitfire.davdroid.ui.TasksActivity
|
||||
import at.bitfire.davdroid.ui.TasksCard
|
||||
import at.bitfire.davdroid.ui.TasksModel
|
||||
import at.bitfire.davdroid.util.TaskUtils
|
||||
import javax.inject.Inject
|
||||
|
||||
|
@ -18,7 +18,7 @@ class TasksIntroPage @Inject constructor(
|
|||
): IntroPage {
|
||||
|
||||
override fun getShowPolicy(): IntroPage.ShowPolicy {
|
||||
return if (TaskUtils.isAvailable(application) || settingsManager.getBooleanOrNull(TasksActivity.Model.HINT_OPENTASKS_NOT_INSTALLED) == false)
|
||||
return if (TaskUtils.isAvailable(application) || settingsManager.getBooleanOrNull(TasksModel.HINT_OPENTASKS_NOT_INSTALLED) == false)
|
||||
IntroPage.ShowPolicy.DONT_SHOW
|
||||
else
|
||||
IntroPage.ShowPolicy.SHOW_ALWAYS
|
||||
|
|
Loading…
Reference in a new issue