mirror of https://github.com/tasks/tasks
Convert ListPicker to compose
parent
5f3b706b81
commit
d157bbec53
@ -0,0 +1,106 @@
|
|||||||
|
package org.tasks.activities
|
||||||
|
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import androidx.lifecycle.ViewModel
|
||||||
|
import androidx.lifecycle.viewModelScope
|
||||||
|
import com.todoroo.astrid.api.Filter
|
||||||
|
import com.todoroo.astrid.api.FilterListItem
|
||||||
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
|
import kotlinx.coroutines.flow.update
|
||||||
|
import kotlinx.coroutines.launch
|
||||||
|
import org.tasks.LocalBroadcastManager
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.billing.Inventory
|
||||||
|
import org.tasks.data.CaldavDao
|
||||||
|
import org.tasks.data.GoogleTaskDao
|
||||||
|
import org.tasks.filters.FilterProvider
|
||||||
|
import org.tasks.filters.NavigationDrawerSubheader
|
||||||
|
import org.tasks.preferences.Preferences
|
||||||
|
import org.tasks.themes.ColorProvider
|
||||||
|
import org.tasks.themes.CustomIcons
|
||||||
|
import javax.inject.Inject
|
||||||
|
|
||||||
|
@HiltViewModel
|
||||||
|
class ListPickerViewModel @Inject constructor(
|
||||||
|
@ApplicationContext private val context: Context,
|
||||||
|
private val filterProvider: FilterProvider,
|
||||||
|
private val localBroadcastManager: LocalBroadcastManager,
|
||||||
|
private val inventory: Inventory,
|
||||||
|
private val colorProvider: ColorProvider,
|
||||||
|
private val preferences: Preferences,
|
||||||
|
private val googleTaskDao: GoogleTaskDao,
|
||||||
|
private val caldavDao: CaldavDao,
|
||||||
|
) : ViewModel() {
|
||||||
|
data class ViewState(
|
||||||
|
val filters: List<FilterListItem> = emptyList(),
|
||||||
|
)
|
||||||
|
|
||||||
|
private val refreshReceiver: BroadcastReceiver = object : BroadcastReceiver() {
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private val _viewState = MutableStateFlow(ViewState())
|
||||||
|
val viewState: StateFlow<ViewState>
|
||||||
|
get() = _viewState.asStateFlow()
|
||||||
|
|
||||||
|
fun onClick(subheader: NavigationDrawerSubheader) = viewModelScope.launch {
|
||||||
|
val collapsed = !subheader.isCollapsed
|
||||||
|
when (subheader.subheaderType) {
|
||||||
|
NavigationDrawerSubheader.SubheaderType.PREFERENCE ->
|
||||||
|
preferences.setBoolean(subheader.id.toInt(), collapsed)
|
||||||
|
NavigationDrawerSubheader.SubheaderType.GOOGLE_TASKS ->
|
||||||
|
googleTaskDao.setCollapsed(subheader.id, collapsed)
|
||||||
|
NavigationDrawerSubheader.SubheaderType.CALDAV,
|
||||||
|
NavigationDrawerSubheader.SubheaderType.TASKS,
|
||||||
|
NavigationDrawerSubheader.SubheaderType.ETESYNC ->
|
||||||
|
caldavDao.setCollapsed(subheader.id, collapsed)
|
||||||
|
}
|
||||||
|
localBroadcastManager.broadcastRefreshList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun refresh() {
|
||||||
|
viewModelScope.launch {
|
||||||
|
val items = filterProvider.listPickerItems()
|
||||||
|
_viewState.update {
|
||||||
|
it.copy(filters = items)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getIcon(filter: Filter): Int {
|
||||||
|
if (filter.icon < 1000 || inventory.hasPro) {
|
||||||
|
val icon = CustomIcons.getIconResId(filter.icon)
|
||||||
|
if (icon != null) {
|
||||||
|
return icon
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return R.drawable.ic_list_24px
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getColor(filter: Filter): Int {
|
||||||
|
if (filter.tint != 0) {
|
||||||
|
val color = colorProvider.getThemeColor(filter.tint, true)
|
||||||
|
if (color.isFree || inventory.purchasedThemes()) {
|
||||||
|
return color.primaryColor
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return context.getColor(R.color.text_primary)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onCleared() {
|
||||||
|
localBroadcastManager.unregisterReceiver(refreshReceiver)
|
||||||
|
}
|
||||||
|
|
||||||
|
init {
|
||||||
|
localBroadcastManager.registerRefreshListReceiver(refreshReceiver)
|
||||||
|
refresh()
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,55 @@
|
|||||||
|
package org.tasks.compose.pickers
|
||||||
|
|
||||||
|
import androidx.compose.animation.core.FastOutLinearInEasing
|
||||||
|
import androidx.compose.animation.core.animateFloatAsState
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.material.ContentAlpha
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.material.icons.Icons
|
||||||
|
import androidx.compose.material.icons.outlined.KeyboardArrowUp
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.getValue
|
||||||
|
import androidx.compose.ui.Alignment
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.rotate
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun CollapsibleRow(
|
||||||
|
text: String,
|
||||||
|
collapsed: Boolean,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clickable { onClick() }
|
||||||
|
.padding(horizontal = 16.dp, vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = text,
|
||||||
|
style = MaterialTheme.typography.body2.copy(
|
||||||
|
fontWeight = FontWeight.Medium,
|
||||||
|
),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
val rotationAngle by animateFloatAsState(
|
||||||
|
targetValue = if (collapsed) -180f else 0f,
|
||||||
|
animationSpec = tween(durationMillis = 250,easing = FastOutLinearInEasing)
|
||||||
|
)
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Outlined.KeyboardArrowUp,
|
||||||
|
contentDescription = null,
|
||||||
|
tint = MaterialTheme.colors.onSurface.copy(alpha = ContentAlpha.medium),
|
||||||
|
modifier = Modifier.rotate(rotationAngle)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,82 @@
|
|||||||
|
package org.tasks.compose.pickers
|
||||||
|
|
||||||
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.Row
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.rememberScrollState
|
||||||
|
import androidx.compose.foundation.verticalScroll
|
||||||
|
import androidx.compose.material.ContentAlpha
|
||||||
|
import androidx.compose.material.Icon
|
||||||
|
import androidx.compose.material.MaterialTheme
|
||||||
|
import androidx.compose.material.Text
|
||||||
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.ui.Alignment.Companion.CenterVertically
|
||||||
|
import androidx.compose.ui.Modifier
|
||||||
|
import androidx.compose.ui.draw.alpha
|
||||||
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.res.painterResource
|
||||||
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||||
|
import com.todoroo.astrid.api.Filter
|
||||||
|
import org.tasks.R
|
||||||
|
import org.tasks.activities.ListPickerViewModel
|
||||||
|
import org.tasks.compose.collectAsStateLifecycleAware
|
||||||
|
import org.tasks.filters.NavigationDrawerSubheader
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun ListPicker(
|
||||||
|
viewModel: ListPickerViewModel = viewModel(),
|
||||||
|
selected: Filter?,
|
||||||
|
onSelected: (Filter) -> Unit,
|
||||||
|
) {
|
||||||
|
val filters = viewModel.viewState.collectAsStateLifecycleAware().value.filters
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.verticalScroll(rememberScrollState())
|
||||||
|
.padding(vertical = 12.dp)
|
||||||
|
) {
|
||||||
|
filters.forEach { filter ->
|
||||||
|
when (filter) {
|
||||||
|
is NavigationDrawerSubheader -> {
|
||||||
|
CollapsibleRow(
|
||||||
|
text = filter.listingTitle,
|
||||||
|
collapsed = filter.isCollapsed,
|
||||||
|
onClick = { viewModel.onClick(filter) },
|
||||||
|
)
|
||||||
|
}
|
||||||
|
is Filter -> {
|
||||||
|
CheckableIconRow(
|
||||||
|
icon = painterResource(id = viewModel.getIcon(filter)),
|
||||||
|
tint = Color(viewModel.getColor(filter)),
|
||||||
|
selected = filter == selected,
|
||||||
|
onClick = { onSelected(filter) },
|
||||||
|
) {
|
||||||
|
Row(verticalAlignment = CenterVertically) {
|
||||||
|
Text(
|
||||||
|
text = filter.listingTitle,
|
||||||
|
style = MaterialTheme.typography.body2.copy(
|
||||||
|
fontWeight = FontWeight.Medium
|
||||||
|
),
|
||||||
|
modifier = Modifier.weight(1f),
|
||||||
|
)
|
||||||
|
if (filter.principals > 0) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
id = when (filter.principals) {
|
||||||
|
1 -> R.drawable.ic_outline_perm_identity_24px
|
||||||
|
in 2..Int.MAX_VALUE -> R.drawable.ic_outline_people_outline_24
|
||||||
|
else -> 0
|
||||||
|
}
|
||||||
|
),
|
||||||
|
modifier = Modifier.alpha(ContentAlpha.medium),
|
||||||
|
contentDescription = null,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@ -0,0 +1,5 @@
|
|||||||
|
<vector android:height="24dp" android:tint="#000000"
|
||||||
|
android:viewportHeight="24" android:viewportWidth="24"
|
||||||
|
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
|
<path android:fillColor="@android:color/white" android:pathData="M12,2C6.48,2 2,6.48 2,12s4.48,10 10,10 10,-4.48 10,-10S17.52,2 12,2zM4,12c0,-4.42 3.58,-8 8,-8 1.85,0 3.55,0.63 4.9,1.69L5.69,16.9C4.63,15.55 4,13.85 4,12zM12,20c-1.85,0 -3.55,-0.63 -4.9,-1.69L18.31,7.1C19.37,8.45 20,10.15 20,12c0,4.42 -3.58,8 -8,8z"/>
|
||||||
|
</vector>
|
||||||
Loading…
Reference in New Issue