Refactor task list banners

pull/3226/head
Alex Baker 11 months ago
parent 80f24fdc17
commit 791041cc5d

@ -38,7 +38,7 @@ class TaskListViewModelTest : InjectingTestCase() {
override fun setUp() { override fun setUp() {
super.setUp() super.setUp()
viewModel = TaskListViewModel( viewModel = TaskListViewModel(
context = context, applicationContext = context,
preferences = preferences, preferences = preferences,
taskDao = taskDao, taskDao = taskDao,
deletionDao = deletionDao, deletionDao = deletionDao,

@ -27,8 +27,9 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.view.ActionMode import androidx.appcompat.view.ActionMode
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.AnimatedVisibility
import androidx.compose.runtime.getValue import androidx.compose.animation.expandVertically
import androidx.compose.animation.shrinkVertically
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.app.ShareCompat import androidx.core.app.ShareCompat
@ -49,7 +50,6 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener import androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener
import com.google.accompanist.permissions.ExperimentalPermissionsApi import com.google.accompanist.permissions.ExperimentalPermissionsApi
import com.google.accompanist.permissions.PermissionStatus
import com.google.accompanist.permissions.rememberPermissionState import com.google.accompanist.permissions.rememberPermissionState
import com.google.accompanist.permissions.shouldShowRationale import com.google.accompanist.permissions.shouldShowRationale
import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.AppBarLayout
@ -91,7 +91,6 @@ import org.tasks.compose.FilterSelectionActivity.Companion.registerForListPicker
import org.tasks.compose.NotificationsDisabledBanner import org.tasks.compose.NotificationsDisabledBanner
import org.tasks.compose.QuietHoursBanner import org.tasks.compose.QuietHoursBanner
import org.tasks.compose.SubscriptionNagBanner import org.tasks.compose.SubscriptionNagBanner
import org.tasks.compose.rememberReminderPermissionState
import org.tasks.data.TaskContainer import org.tasks.data.TaskContainer
import org.tasks.data.dao.CaldavDao import org.tasks.data.dao.CaldavDao
import org.tasks.data.dao.TagDataDao import org.tasks.data.dao.TagDataDao
@ -142,6 +141,7 @@ import org.tasks.themes.TasksTheme
import org.tasks.themes.Theme import org.tasks.themes.Theme
import org.tasks.themes.ThemeColor import org.tasks.themes.ThemeColor
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.ui.Banner
import org.tasks.ui.TaskEditEvent import org.tasks.ui.TaskEditEvent
import org.tasks.ui.TaskEditEventBus import org.tasks.ui.TaskEditEventBus
import org.tasks.ui.TaskListEvent import org.tasks.ui.TaskListEvent
@ -269,7 +269,7 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
.launchIn(viewLifecycleOwner.lifecycleScope) .launchIn(viewLifecycleOwner.lifecycleScope)
} }
@OptIn(ExperimentalAnimationApi::class, ExperimentalPermissionsApi::class) @OptIn(ExperimentalPermissionsApi::class)
override fun onCreateView( override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
requireActivity().onBackPressedDispatcher.addCallback(owner = viewLifecycleOwner) { requireActivity().onBackPressedDispatcher.addCallback(owner = viewLifecycleOwner) {
@ -362,26 +362,28 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
val context = LocalContext.current val context = LocalContext.current
val state = listViewModel.state.collectAsStateWithLifecycle().value val state = listViewModel.state.collectAsStateWithLifecycle().value
TasksTheme(theme = theme.themeBase.index) { TasksTheme(theme = theme.themeBase.index) {
val hasRemindersPermission by rememberReminderPermissionState()
val notificationPermissions = if (AndroidUtilities.atLeastTiramisu()) { val notificationPermissions = if (AndroidUtilities.atLeastTiramisu()) {
rememberPermissionState( rememberPermissionState(
Manifest.permission.POST_NOTIFICATIONS, Manifest.permission.POST_NOTIFICATIONS,
onPermissionResult = { success -> onPermissionResult = { success ->
if (success) { if (success) {
NotificationSchedulerIntentService.enqueueWork(context) NotificationSchedulerIntentService.enqueueWork(context)
listViewModel.dismissNotificationBanner(fix = true) listViewModel.dismissBanner(tookAction = true)
} }
} }
) )
} else { } else {
null null
} }
val showNotificationBanner = state.warnNotificationsDisabled && notificationPermissions?.status is PermissionStatus.Denied
val showAlarmsBanner = !showNotificationBanner && state.warnNotificationsDisabled && !hasRemindersPermission AnimatedVisibility(
val showSubscriptionNag = !showNotificationBanner && !showAlarmsBanner && state.begForSubscription visible = state.banner != null,
val showQuietHoursWarning = !showNotificationBanner && !showAlarmsBanner && !showSubscriptionNag && state.warnQuietHoursEnabled enter = expandVertically(),
exit = shrinkVertically(),
) {
when (state.banner) {
is Banner.NotificationsDisabled ->
NotificationsDisabledBanner( NotificationsDisabledBanner(
visible = showNotificationBanner,
settings = { settings = {
if (notificationPermissions?.status?.shouldShowRationale == true) { if (notificationPermissions?.status?.shouldShowRationale == true) {
context.openAppNotificationSettings() context.openAppNotificationSettings()
@ -389,38 +391,55 @@ class TaskListFragment : Fragment(), OnRefreshListener, Toolbar.OnMenuItemClickL
notificationPermissions?.launchPermissionRequest() notificationPermissions?.launchPermissionRequest()
} }
}, },
dismiss = { listViewModel.dismissNotificationBanner() }, dismiss = { listViewModel.dismissBanner() },
) )
Banner.AlarmsDisabled ->
AlarmsDisabledBanner( AlarmsDisabledBanner(
visible = showAlarmsBanner,
settings = { context.openReminderSettings() }, settings = { context.openReminderSettings() },
dismiss = { listViewModel.dismissNotificationBanner() }, dismiss = { listViewModel.dismissBanner() },
) )
Banner.BegForMoney ->
SubscriptionNagBanner( SubscriptionNagBanner(
visible = showSubscriptionNag,
subscribe = { subscribe = {
listViewModel.dismissPurchaseBanner(clickedPurchase = true) listViewModel.dismissBanner(tookAction = true)
if (TasksApplication.IS_GOOGLE_PLAY) { if (TasksApplication.IS_GOOGLE_PLAY) {
context.startActivity(Intent(context, PurchaseActivity::class.java)) context.startActivity(
Intent(
context,
PurchaseActivity::class.java
)
)
} else { } else {
preferences.lastSubscribeRequest = currentTimeMillis() preferences.lastSubscribeRequest = currentTimeMillis()
context.openUri(R.string.url_donate) context.openUri(R.string.url_donate)
} }
}, },
dismiss = { dismiss = {
listViewModel.dismissPurchaseBanner(clickedPurchase = false) listViewModel.dismissBanner()
}, },
) )
Banner.QuietHoursEnabled ->
QuietHoursBanner( QuietHoursBanner(
visible = showQuietHoursWarning,
showSettings = { showSettings = {
listViewModel.dismissQuietHoursBanner() listViewModel.dismissBanner()
context.startActivity(Intent(context, MainPreferences::class.java)) context.startActivity(
Intent(
context,
MainPreferences::class.java
)
)
}, },
dismiss = { dismiss = {
listViewModel.dismissQuietHoursBanner() listViewModel.dismissBanner()
} }
) )
null -> {}
}
}
} }
} }
return binding.root return binding.root

@ -1,24 +1,21 @@
package org.tasks.compose package org.tasks.compose
import android.content.res.Configuration import android.content.res.Configuration
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import org.tasks.R import org.tasks.R
import org.tasks.TasksApplication import org.tasks.TasksApplication
import org.tasks.compose.components.AnimatedBanner import org.tasks.compose.components.AnimatedBanner
import org.tasks.compose.components.Banner
import org.tasks.themes.TasksTheme import org.tasks.themes.TasksTheme
@ExperimentalAnimationApi
@Composable @Composable
fun NotificationsDisabledBanner( fun NotificationsDisabledBanner(
visible: Boolean,
settings: () -> Unit, settings: () -> Unit,
dismiss: () -> Unit, dismiss: () -> Unit,
) { ) {
AnimatedBanner( Banner(
visible = visible,
title = stringResource(id = R.string.enable_reminders), title = stringResource(id = R.string.enable_reminders),
body = stringResource(id = R.string.enable_reminders_description), body = stringResource(id = R.string.enable_reminders_description),
dismissText = stringResource(id = R.string.dismiss), dismissText = stringResource(id = R.string.dismiss),
@ -28,15 +25,12 @@ fun NotificationsDisabledBanner(
) )
} }
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun AlarmsDisabledBanner( fun AlarmsDisabledBanner(
visible: Boolean,
settings: () -> Unit, settings: () -> Unit,
dismiss: () -> Unit, dismiss: () -> Unit,
) { ) {
AnimatedBanner( Banner(
visible = visible,
title = stringResource(id = R.string.enable_alarms), title = stringResource(id = R.string.enable_alarms),
body = stringResource(id = R.string.enable_alarms_description), body = stringResource(id = R.string.enable_alarms_description),
dismissText = stringResource(id = R.string.dismiss), dismissText = stringResource(id = R.string.dismiss),
@ -47,15 +41,12 @@ fun AlarmsDisabledBanner(
} }
@ExperimentalAnimationApi
@Composable @Composable
fun SubscriptionNagBanner( fun SubscriptionNagBanner(
visible: Boolean,
subscribe: () -> Unit, subscribe: () -> Unit,
dismiss: () -> Unit, dismiss: () -> Unit,
) { ) {
AnimatedBanner( Banner(
visible = visible,
title = stringResource(id = R.string.enjoying_tasks), title = stringResource(id = R.string.enjoying_tasks),
body = stringResource(id = if (TasksApplication.IS_GENERIC) { body = stringResource(id = if (TasksApplication.IS_GENERIC) {
R.string.donate_nag R.string.donate_nag
@ -73,15 +64,12 @@ fun SubscriptionNagBanner(
) )
} }
@OptIn(ExperimentalAnimationApi::class)
@Composable @Composable
fun QuietHoursBanner( fun QuietHoursBanner(
visible: Boolean,
showSettings: () -> Unit, showSettings: () -> Unit,
dismiss: () -> Unit, dismiss: () -> Unit,
) { ) {
AnimatedBanner( Banner(
visible = visible,
title = stringResource(R.string.quiet_hours_in_effect), title = stringResource(R.string.quiet_hours_in_effect),
body = stringResource(R.string.quiet_hours_summary), body = stringResource(R.string.quiet_hours_summary),
dismissText = stringResource(id = R.string.dismiss), dismissText = stringResource(id = R.string.dismiss),
@ -91,7 +79,6 @@ fun QuietHoursBanner(
) )
} }
@ExperimentalAnimationApi
@Composable @Composable
fun BeastModeBanner( fun BeastModeBanner(
visible: Boolean, visible: Boolean,
@ -109,15 +96,13 @@ fun BeastModeBanner(
) )
} }
@ExperimentalAnimationApi
@Preview(showBackground = true) @Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun NotificationsDisabledPreview() = TasksTheme { private fun NotificationsDisabledPreview() = TasksTheme {
NotificationsDisabledBanner(visible = true, settings = {}, dismiss = {}) NotificationsDisabledBanner(settings = {}, dismiss = {})
} }
@ExperimentalAnimationApi
@Preview(showBackground = true) @Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
@ -125,18 +110,16 @@ private fun BeastModePreview() = TasksTheme {
BeastModeBanner(visible = true, showSettings = {}, dismiss = {}) BeastModeBanner(visible = true, showSettings = {}, dismiss = {})
} }
@ExperimentalAnimationApi
@Preview(showBackground = true) @Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun SubscriptionNagPreview() = TasksTheme { private fun SubscriptionNagPreview() = TasksTheme {
SubscriptionNagBanner(visible = true, subscribe = {}, dismiss = {}) SubscriptionNagBanner(subscribe = {}, dismiss = {})
} }
@ExperimentalAnimationApi
@Preview(showBackground = true) @Preview(showBackground = true)
@Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES) @Preview(showBackground = true, backgroundColor = 0x202124, uiMode = Configuration.UI_MODE_NIGHT_YES)
@Composable @Composable
private fun QuietHoursPreview() = TasksTheme { private fun QuietHoursPreview() = TasksTheme {
QuietHoursBanner(visible = true, showSettings = {}, dismiss = {}) QuietHoursBanner(showSettings = {}, dismiss = {})
} }

@ -42,9 +42,13 @@ public class PermissionChecker {
return checkPermissions(backgroundPermissions().toArray(new String[0])); return checkPermissions(backgroundPermissions().toArray(new String[0]));
} }
public boolean hasNotificationPermission() {
return !atLeastTiramisu() || checkPermissions(permission.POST_NOTIFICATIONS);
}
public boolean canNotify() { public boolean canNotify() {
return org.tasks.extensions.Context.INSTANCE.canScheduleExactAlarms(context) return org.tasks.extensions.Context.INSTANCE.canScheduleExactAlarms(context)
&& (!atLeastTiramisu() || checkPermissions(permission.POST_NOTIFICATIONS)); && hasNotificationPermission();
} }
private boolean checkPermissions(String... permissions) { private boolean checkPermissions(String... permissions) {

@ -562,6 +562,10 @@ class Preferences @JvmOverloads constructor(
get() = getBoolean(R.string.p_warn_notifications_disabled, true) get() = getBoolean(R.string.p_warn_notifications_disabled, true)
set(value) = setBoolean(R.string.p_warn_notifications_disabled, value) set(value) = setBoolean(R.string.p_warn_notifications_disabled, value)
var warnAlarmsDisabled: Boolean
get() = getBoolean(R.string.p_warn_alarms_disabled, true)
set(value) = setBoolean(R.string.p_warn_alarms_disabled, value)
var warnQuietHoursDisabled: Boolean var warnQuietHoursDisabled: Boolean
get() = getBoolean(R.string.p_warn_quiet_hours_enabled, true) get() = getBoolean(R.string.p_warn_quiet_hours_enabled, true)
set(value) = setBoolean(R.string.p_warn_quiet_hours_enabled, value) set(value) = setBoolean(R.string.p_warn_quiet_hours_enabled, value)

@ -1,6 +1,5 @@
package org.tasks.ui package org.tasks.ui
import android.annotation.SuppressLint
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
@ -32,12 +31,14 @@ import org.tasks.data.dao.TaskDao
import org.tasks.data.entity.Task import org.tasks.data.entity.Task
import org.tasks.data.fetchTasks import org.tasks.data.fetchTasks
import org.tasks.db.QueryUtils import org.tasks.db.QueryUtils
import org.tasks.extensions.Context.canScheduleExactAlarms
import org.tasks.filters.AstridOrderingFilter import org.tasks.filters.AstridOrderingFilter
import org.tasks.filters.EmptyFilter import org.tasks.filters.EmptyFilter
import org.tasks.filters.Filter import org.tasks.filters.Filter
import org.tasks.filters.FilterImpl import org.tasks.filters.FilterImpl
import org.tasks.filters.MyTasksFilter import org.tasks.filters.MyTasksFilter
import org.tasks.filters.SearchFilter import org.tasks.filters.SearchFilter
import org.tasks.preferences.PermissionChecker
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.preferences.QueryPreferences import org.tasks.preferences.QueryPreferences
import org.tasks.tasklist.SectionedDataSource import org.tasks.tasklist.SectionedDataSource
@ -45,10 +46,16 @@ import org.tasks.tasklist.TasksResults
import org.tasks.time.DateTimeUtils2.currentTimeMillis import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject import javax.inject.Inject
sealed interface Banner {
data object NotificationsDisabled : Banner
data object AlarmsDisabled : Banner
data object QuietHoursEnabled : Banner
data object BegForMoney : Banner
}
@HiltViewModel @HiltViewModel
@SuppressLint("StaticFieldLeak")
class TaskListViewModel @Inject constructor( class TaskListViewModel @Inject constructor(
@ApplicationContext context: Context, @ApplicationContext private val applicationContext: Context,
private val preferences: Preferences, private val preferences: Preferences,
private val taskDao: TaskDao, private val taskDao: TaskDao,
private val taskDeleter: TaskDeleter, private val taskDeleter: TaskDeleter,
@ -56,19 +63,17 @@ class TaskListViewModel @Inject constructor(
private val localBroadcastManager: LocalBroadcastManager, private val localBroadcastManager: LocalBroadcastManager,
private val inventory: Inventory, private val inventory: Inventory,
private val firebase: Firebase, private val firebase: Firebase,
private val permissionChecker: PermissionChecker,
) : ViewModel() { ) : ViewModel() {
data class State( data class State(
val filter: Filter = EmptyFilter(), val filter: Filter = EmptyFilter(),
val now: Long = currentTimeMillis(), val now: Long = currentTimeMillis(),
val searchQuery: String? = null, val searchQuery: String? = null,
val tasks: TasksResults = TasksResults.Loading, val tasks: TasksResults = TasksResults.Loading,
val begForSubscription: Boolean = false,
val warnNotificationsDisabled: Boolean = false,
val syncOngoing: Boolean = false, val syncOngoing: Boolean = false,
val warnQuietHoursEnabled: Boolean = false,
val collapsed: Set<Long> = setOf(SectionedDataSource.HEADER_COMPLETED), val collapsed: Set<Long> = setOf(SectionedDataSource.HEADER_COMPLETED),
val banner: Banner? = null
) )
private val _state = MutableStateFlow(State()) private val _state = MutableStateFlow(State())
@ -99,23 +104,6 @@ class TaskListViewModel @Inject constructor(
} }
} }
fun dismissNotificationBanner(
fix: Boolean = false,
) {
_state.update {
it.copy(warnNotificationsDisabled = false)
}
preferences.warnNotificationsDisabled = fix
}
fun dismissPurchaseBanner(clickedPurchase: Boolean) {
_state.update {
it.copy(begForSubscription = false)
}
preferences.lastSubscribeRequest = currentTimeMillis()
firebase.logEvent(R.string.event_banner_sub, R.string.param_click to clickedPurchase)
}
suspend fun getTasksToClear(): List<Long> { suspend fun getTasksToClear(): List<Long> {
val filter = _state.value.filter val filter = _state.value.filter
val deleteFilter = FilterImpl( val deleteFilter = FilterImpl(
@ -150,7 +138,7 @@ class TaskListViewModel @Inject constructor(
val filter = when { val filter = when {
it.searchQuery == null -> it.filter it.searchQuery == null -> it.filter
it.searchQuery.isBlank() -> MyTasksFilter.create() it.searchQuery.isBlank() -> MyTasksFilter.create()
else -> context.createSearchQuery(it.searchQuery) else -> applicationContext.createSearchQuery(it.searchQuery)
} }
taskDao.fetchTasks { getQuery(preferences, filter) } taskDao.fetchTasks { getQuery(preferences, filter) }
} }
@ -200,26 +188,36 @@ class TaskListViewModel @Inject constructor(
fun updateBannerState() { fun updateBannerState() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
_state.update { val banner = when {
it.copy( preferences.warnNotificationsDisabled && !permissionChecker.hasNotificationPermission() ->
warnNotificationsDisabled = preferences.warnNotificationsDisabled, Banner.NotificationsDisabled
warnQuietHoursEnabled = preferences.isCurrentlyQuietHours && preferences.warnQuietHoursDisabled, preferences.warnAlarmsDisabled && !applicationContext.canScheduleExactAlarms() ->
) Banner.AlarmsDisabled
(IS_GENERIC || !inventory.hasPro) && !firebase.subscribeCooldown ->
Banner.BegForMoney
preferences.isCurrentlyQuietHours && preferences.warnQuietHoursDisabled ->
Banner.QuietHoursEnabled
else -> null
} }
if ((IS_GENERIC || !inventory.hasPro) && !firebase.subscribeCooldown) {
_state.update { _state.update {
it.copy(begForSubscription = true) it.copy(banner = banner)
}
} }
} }
} }
fun dismissQuietHoursBanner() = viewModelScope.launch(Dispatchers.IO) { fun dismissBanner(tookAction: Boolean = false) {
preferences.warnQuietHoursDisabled = false when (state.value.banner) {
_state.update { Banner.NotificationsDisabled ->preferences.warnNotificationsDisabled = tookAction
it.copy(warnQuietHoursEnabled = false) Banner.AlarmsDisabled -> preferences.warnAlarmsDisabled = false
Banner.QuietHoursEnabled -> preferences.warnQuietHoursDisabled = false
Banner.BegForMoney -> {
preferences.lastSubscribeRequest = currentTimeMillis()
firebase.logEvent(R.string.event_banner_sub, R.string.param_click to tookAction)
} }
else -> {}
}
updateBannerState()
} }
companion object { companion object {

@ -407,6 +407,7 @@
<string name="p_install_date">install_date</string> <string name="p_install_date">install_date</string>
<string name="p_default_location">default_location</string> <string name="p_default_location">default_location</string>
<string name="p_warn_notifications_disabled">warn_notifications_disabled</string> <string name="p_warn_notifications_disabled">warn_notifications_disabled</string>
<string name="p_warn_alarms_disabled">warn_alarms_disabled</string>
<string name="p_warn_quiet_hours_enabled">warn_quiet_hours_enabled</string> <string name="p_warn_quiet_hours_enabled">warn_quiet_hours_enabled</string>
<string name="event_showed_purchase_dialog">showed_purchase_dialog</string> <string name="event_showed_purchase_dialog">showed_purchase_dialog</string>

@ -1,3 +1,5 @@
@file:OptIn(ExperimentalAnimationApi::class)
package org.tasks.compose.components package org.tasks.compose.components
import androidx.compose.animation.AnimatedVisibility import androidx.compose.animation.AnimatedVisibility
@ -19,7 +21,6 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
@ExperimentalAnimationApi
@Composable @Composable
fun AnimatedBanner( fun AnimatedBanner(
visible: Boolean, visible: Boolean,
@ -30,8 +31,32 @@ fun AnimatedBanner(
action: String, action: String,
onAction: () -> Unit, onAction: () -> Unit,
) { ) {
AnimatedBanner( AnimatedVisibility(
visible = visible, visible = visible,
enter = expandVertically(),
exit = shrinkVertically(),
) {
Banner(
title = title,
body = body,
dismissText = dismissText,
onDismiss = onDismiss,
action = action,
onAction = onAction,
)
}
}
@Composable
fun Banner(
title: String,
body: String,
dismissText: String? = null,
onDismiss: () -> Unit,
action: String,
onAction: () -> Unit,
) {
Banner(
content = { content = {
Text( Text(
text = title, text = title,
@ -58,15 +83,9 @@ fun AnimatedBanner(
@ExperimentalAnimationApi @ExperimentalAnimationApi
@Composable @Composable
private fun AnimatedBanner( private fun Banner(
visible: Boolean,
content: @Composable () -> Unit, content: @Composable () -> Unit,
buttons: @Composable () -> Unit, buttons: @Composable () -> Unit,
) {
AnimatedVisibility(
visible = visible,
enter = expandVertically(),
exit = shrinkVertically(),
) { ) {
Card( Card(
modifier = Modifier modifier = Modifier
@ -85,7 +104,6 @@ private fun AnimatedBanner(
} }
} }
} }
}
@Composable @Composable
fun BannerTextButton(text: String, onClick: () -> Unit) { fun BannerTextButton(text: String, onClick: () -> Unit) {

Loading…
Cancel
Save