diff --git a/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt b/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt
index b153c4b7f..00a51bc2c 100755
--- a/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt
+++ b/app/src/main/java/com/todoroo/astrid/activity/TaskEditFragment.kt
@@ -1,48 +1,44 @@
-/*
- * Copyright (c) 2012 Todoroo Inc
- *
- * See the file "LICENSE" for the full license governing this code.
- */
package com.todoroo.astrid.activity
import android.app.Activity
-import android.content.Context
import android.content.Intent
-import android.graphics.Paint
import android.os.Bundle
import android.view.LayoutInflater
-import android.view.MenuItem
import android.view.View
import android.view.ViewGroup
-import android.view.inputmethod.EditorInfo
-import android.view.inputmethod.InputMethodManager
-import androidx.activity.addCallback
+import androidx.activity.compose.BackHandler
import androidx.activity.result.contract.ActivityResultContracts
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Column
-import androidx.compose.foundation.layout.fillMaxWidth
-import androidx.compose.material3.Divider
+import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
+import androidx.compose.material.icons.Icons
+import androidx.compose.material.icons.automirrored.outlined.ArrowBack
+import androidx.compose.material.icons.outlined.Clear
+import androidx.compose.material.icons.outlined.Delete
+import androidx.compose.material.icons.outlined.Save
+import androidx.compose.material3.ExperimentalMaterial3Api
+import androidx.compose.material3.HorizontalDivider
+import androidx.compose.material3.Icon
+import androidx.compose.material3.IconButton
+import androidx.compose.material3.Scaffold
+import androidx.compose.material3.TopAppBar
import androidx.compose.runtime.Composable
-import androidx.compose.runtime.getValue
-import androidx.compose.runtime.mutableStateOf
-import androidx.compose.runtime.saveable.rememberSaveable
-import androidx.compose.runtime.setValue
+import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.pointer.PointerEventPass
import androidx.compose.ui.input.pointer.pointerInput
+import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.viewinterop.AndroidViewBinding
-import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.os.BundleCompat
-import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.lifecycle.lifecycleScope
-import com.google.android.material.appbar.AppBarLayout
-import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback
import com.todoroo.andlib.utility.AndroidUtilities.atLeastOreoMR1
import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.files.FilesControlSet
@@ -75,8 +71,8 @@ import org.tasks.data.dao.UserActivityDao
import org.tasks.data.entity.Alarm
import org.tasks.data.entity.TagData
import org.tasks.data.entity.Task
-import org.tasks.databinding.FragmentTaskEditBinding
import org.tasks.databinding.TaskEditCalendarBinding
+import org.tasks.databinding.TaskEditCommentBarBinding
import org.tasks.databinding.TaskEditFilesBinding
import org.tasks.databinding.TaskEditLocationBinding
import org.tasks.databinding.TaskEditRemindersBinding
@@ -106,6 +102,7 @@ import org.tasks.notifications.NotificationManager
import org.tasks.play.PlayServices
import org.tasks.preferences.Preferences
import org.tasks.themes.TasksTheme
+import org.tasks.themes.Theme
import org.tasks.ui.CalendarControlSet
import org.tasks.ui.ChipProvider
import org.tasks.ui.LocationControlSet
@@ -116,10 +113,9 @@ import org.tasks.ui.TaskEditViewModel
import org.tasks.ui.TaskEditViewModel.Companion.stripCarriageReturns
import java.util.Locale
import javax.inject.Inject
-import kotlin.math.abs
@AndroidEntryPoint
-class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
+class TaskEditFragment : Fragment() {
@Inject lateinit var taskDao: TaskDao
@Inject lateinit var userActivityDao: UserActivityDao
@Inject lateinit var notificationManager: NotificationManager
@@ -134,9 +130,9 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
@Inject lateinit var locale: Locale
@Inject lateinit var chipProvider: ChipProvider
@Inject lateinit var playServices: PlayServices
+ @Inject lateinit var theme: Theme
- val editViewModel: TaskEditViewModel by viewModels()
- lateinit var binding: FragmentTaskEditBinding
+ private val editViewModel: TaskEditViewModel by viewModels()
private var showKeyboard = false
private val beastMode =
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
@@ -149,166 +145,141 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
val task: Task?
get() = BundleCompat.getParcelable(requireArguments(), EXTRA_TASK, Task::class.java)
+ @OptIn(ExperimentalAnimationApi::class, ExperimentalMaterial3Api::class)
override fun onCreateView(
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
- requireActivity().onBackPressedDispatcher.addCallback(owner = viewLifecycleOwner) {
- if (preferences.backButtonSavesTask()) {
- lifecycleScope.launch {
- save()
- }
- } else {
- discardButtonClick()
- }
- }
if (atLeastOreoMR1()) {
activity?.setShowWhenLocked(preferences.showEditScreenWithoutUnlock)
}
-
- binding = FragmentTaskEditBinding.inflate(inflater)
- val view: View = binding.root
val model = editViewModel.task
- val toolbar = binding.toolbar
- toolbar.navigationIcon = AppCompatResources.getDrawable(
- context,
- if (editViewModel.isReadOnly)
- R.drawable.ic_outline_arrow_back_24px
- else
- R.drawable.ic_outline_save_24px
- )
- toolbar.setNavigationOnClickListener {
+ if (!model.isNew) {
lifecycleScope.launch {
- save()
+ notificationManager.cancel(model.id)
}
}
- val backButtonSavesTask = preferences.backButtonSavesTask()
- toolbar.setNavigationContentDescription(
- when {
- editViewModel.isReadOnly -> R.string.back
- backButtonSavesTask -> R.string.discard
- else -> R.string.save
- }
- )
- toolbar.inflateMenu(R.menu.menu_task_edit_fragment)
- val menu = toolbar.menu
- val delete = menu.findItem(R.id.menu_delete)
- delete.isVisible = !model.isNew && editViewModel.isWritable
- delete.setShowAsAction(
- if (backButtonSavesTask) MenuItem.SHOW_AS_ACTION_NEVER else MenuItem.SHOW_AS_ACTION_IF_ROOM)
- val discard = menu.findItem(R.id.menu_discard)
- discard.isVisible = backButtonSavesTask && editViewModel.isWritable
- discard.setShowAsAction(
- if (model.isNew) MenuItem.SHOW_AS_ACTION_IF_ROOM else MenuItem.SHOW_AS_ACTION_NEVER)
if (savedInstanceState == null) {
showKeyboard = model.isNew && isNullOrEmpty(model.title)
}
- val params = binding.appbarlayout.layoutParams as CoordinatorLayout.LayoutParams
- params.behavior = AppBarLayout.Behavior()
- val behavior = params.behavior as AppBarLayout.Behavior?
- behavior!!.setDragCallback(object : DragCallback() {
- override fun canDrag(appBarLayout: AppBarLayout): Boolean {
- return false
- }
- })
- toolbar.setOnMenuItemClickListener(this)
- val title = binding.title
- val textWatcher = markdownProvider.markdown(preferences.linkify).textWatcher(title)
- title.addTextChangedListener(
- onTextChanged = { _, _, _, _ ->
- editViewModel.title = title.text.toString().trim { it <= ' ' }
- },
- afterTextChanged = {
- textWatcher?.invoke(it)
- }
- )
- title.setOnEditorActionListener { _, actionId, _ ->
- if (actionId == EditorInfo.IME_ACTION_DONE) {
- lifecycleScope.launch {
- save()
- }
- true
- } else false
- }
- title.setText(model.title)
- title.setHorizontallyScrolling(false)
- title.maxLines = 5
- if (editViewModel.isReadOnly) {
- title.isFocusable = false
- title.isFocusableInTouchMode = false
- title.isCursorVisible = false
- }
- if (
- model.isNew ||
- preferences.getBoolean(R.string.p_hide_check_button, false) ||
- editViewModel.isReadOnly
- ) {
- binding.fab.visibility = View.INVISIBLE
- } else if (editViewModel.completed) {
- title.paintFlags = title.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
- binding.fab.setImageResource(R.drawable.ic_outline_check_box_outline_blank_24px)
- }
- binding.fab.setOnClickListener {
- if (editViewModel.completed) {
- editViewModel.completed = false
- title.paintFlags = title.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
- binding.fab.setImageResource(R.drawable.ic_outline_check_box_24px)
- } else {
- editViewModel.completed = true
- lifecycleScope.launch {
- save()
+ val backButtonSavesTask = preferences.backButtonSavesTask()
+ val view = ComposeView(context).apply {
+ setContent {
+ BackHandler {
+ if (backButtonSavesTask) {
+ lifecycleScope.launch {
+ save()
+ }
+ } else {
+ discardButtonClick()
+ }
}
- }
- }
- binding.appbarlayout.addOnOffsetChangedListener { appBarLayout, verticalOffset ->
- if (verticalOffset == 0) {
- title.visibility = View.VISIBLE
- binding.collapsingtoolbarlayout.isTitleEnabled = false
- } else if (abs(verticalOffset) < appBarLayout.totalScrollRange) {
- title.visibility = View.INVISIBLE
- binding.collapsingtoolbarlayout.title = title.text
- binding.collapsingtoolbarlayout.isTitleEnabled = true
- }
- }
- if (!model.isNew) {
- lifecycleScope.launch {
- notificationManager.cancel(model.id)
- }
- if (preferences.linkify) {
- linkify.linkify(title)
- }
- }
- binding.composeView.setContent {
- TasksTheme {
- Column(modifier = Modifier.gesturesDisabled(editViewModel.isReadOnly)) {
- taskEditControlSetFragmentManager.displayOrder.forEachIndexed { index, tag ->
- if (index < taskEditControlSetFragmentManager.visibleSize) {
- // TODO: remove ui-viewbinding library when these are all migrated
- when (taskEditControlSetFragmentManager.controlSetFragments[tag]) {
- TAG_DUE_DATE -> DueDateRow()
- TAG_PRIORITY -> PriorityRow()
- TAG_DESCRIPTION -> DescriptionRow()
- TAG_LIST -> ListRow()
- TAG_CREATION -> CreationRow()
- CalendarControlSet.TAG -> AndroidViewBinding(TaskEditCalendarBinding::inflate)
- StartDateControlSet.TAG -> AndroidViewBinding(
- TaskEditStartDateBinding::inflate
- )
- ReminderControlSet.TAG -> AndroidViewBinding(
- TaskEditRemindersBinding::inflate
- )
- LocationControlSet.TAG -> AndroidViewBinding(TaskEditLocationBinding::inflate)
- FilesControlSet.TAG -> AndroidViewBinding(TaskEditFilesBinding::inflate)
- TimerControlSet.TAG -> AndroidViewBinding(TaskEditTimerBinding::inflate)
- TagsControlSet.TAG -> AndroidViewBinding(TaskEditTagsBinding::inflate)
- RepeatControlSet.TAG -> AndroidViewBinding(TaskEditRepeatBinding::inflate)
- SubtaskControlSet.TAG -> AndroidViewBinding(TaskEditSubtasksBinding::inflate)
- else -> throw IllegalArgumentException("Unknown row: $tag")
+ TasksTheme(theme = theme.themeBase.index,) {
+ Scaffold(
+ topBar = {
+ TopAppBar(
+ navigationIcon = {
+ IconButton(
+ onClick = {
+ when {
+ editViewModel.isReadOnly -> activity?.onBackPressed()
+ backButtonSavesTask -> discardButtonClick()
+ else -> lifecycleScope.launch { save() }
+ }
+ }
+ ) {
+ Icon(
+ imageVector = when {
+ editViewModel.isReadOnly -> Icons.AutoMirrored.Outlined.ArrowBack
+ backButtonSavesTask -> Icons.Outlined.Clear
+ else -> Icons.Outlined.Save
+ },
+ contentDescription = when {
+ editViewModel.isReadOnly -> stringResource(R.string.back)
+ backButtonSavesTask -> stringResource(R.string.discard)
+ else -> stringResource(R.string.save)
+ }
+ )
+ }
+ },
+ title = {},
+ actions = {
+ if (backButtonSavesTask && editViewModel.isWritable) {
+ IconButton(onClick = { discardButtonClick() }) {
+ Icon(
+ imageVector = Icons.Outlined.Clear,
+ contentDescription = stringResource(R.string.menu_discard_changes),
+ )
+ }
+ }
+ if (!editViewModel.isNew && editViewModel.isWritable) {
+ IconButton(onClick = { deleteButtonClick() }) {
+ Icon(
+ imageVector = Icons.Outlined.Delete,
+ contentDescription = stringResource(R.string.delete_task),
+ )
+ }
+ }
+ },
+ )
+ },
+ bottomBar = {
+ if (preferences.getBoolean(R.string.p_show_task_edit_comments, false)) {
+ AndroidViewBinding(TaskEditCommentBarBinding::inflate)
+ }
+ },
+ ) { paddingValues ->
+ Column(
+ modifier = Modifier
+ .gesturesDisabled(editViewModel.isReadOnly)
+ .padding(paddingValues)
+ .fillMaxSize()
+ .verticalScroll(rememberScrollState()),
+ ) {
+ TitleRow(requestFocus = showKeyboard)
+ HorizontalDivider()
+ taskEditControlSetFragmentManager.displayOrder.forEachIndexed { index, tag ->
+ if (index < taskEditControlSetFragmentManager.visibleSize) {
+ // TODO: remove ui-viewbinding library when these are all migrated
+ when (taskEditControlSetFragmentManager.controlSetFragments[tag]) {
+ TAG_DUE_DATE -> DueDateRow()
+ TAG_PRIORITY -> PriorityRow()
+ TAG_DESCRIPTION -> DescriptionRow()
+ TAG_LIST -> ListRow()
+ TAG_CREATION -> CreationRow()
+ CalendarControlSet.TAG -> AndroidViewBinding(TaskEditCalendarBinding::inflate)
+ StartDateControlSet.TAG -> AndroidViewBinding(
+ TaskEditStartDateBinding::inflate
+ )
+ ReminderControlSet.TAG -> AndroidViewBinding(
+ TaskEditRemindersBinding::inflate
+ )
+ LocationControlSet.TAG -> AndroidViewBinding(TaskEditLocationBinding::inflate)
+ FilesControlSet.TAG -> AndroidViewBinding(TaskEditFilesBinding::inflate)
+ TimerControlSet.TAG -> AndroidViewBinding(TaskEditTimerBinding::inflate)
+ TagsControlSet.TAG -> AndroidViewBinding(TaskEditTagsBinding::inflate)
+ RepeatControlSet.TAG -> AndroidViewBinding(TaskEditRepeatBinding::inflate)
+ SubtaskControlSet.TAG -> AndroidViewBinding(TaskEditSubtasksBinding::inflate)
+ else -> throw IllegalArgumentException("Unknown row: $tag")
+ }
+ HorizontalDivider()
+ }
+ }
+ if (preferences.getBoolean(R.string.p_show_task_edit_comments, false)) {
+ Comments()
}
- Divider(modifier = Modifier.fillMaxWidth())
+ val showBeastModeHint = editViewModel.showBeastModeHint.collectAsStateWithLifecycle().value
+ val context = LocalContext.current
+ BeastModeBanner(
+ showBeastModeHint,
+ showSettings = {
+ editViewModel.hideBeastModeHint(click = true)
+ beastMode.launch(Intent(context, BeastModePreferences::class.java))
+ },
+ dismiss = {
+ editViewModel.hideBeastModeHint(click = false)
+ }
+ )
}
}
- if (preferences.getBoolean(R.string.p_show_task_edit_comments, false)) {
- Comments()
- }
}
}
}
@@ -337,61 +308,14 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
}
- @OptIn(ExperimentalAnimationApi::class)
- private fun showBeastModeHint() {
- binding.banner.setContent {
- var visible by rememberSaveable { mutableStateOf(true) }
- val context = LocalContext.current
- TasksTheme {
- BeastModeBanner(
- visible,
- showSettings = {
- visible = false
- preferences.shownBeastModeHint = true
- beastMode.launch(Intent(context, BeastModePreferences::class.java))
- firebase.logEvent(R.string.event_banner_beast, R.string.param_click to true)
- },
- dismiss = {
- visible = false
- preferences.shownBeastModeHint = true
- firebase.logEvent(R.string.event_banner_beast, R.string.param_click to false)
- }
- )
- }
- }
- }
-
- override fun onResume() {
- super.onResume()
- if (showKeyboard) {
- binding.title.requestFocus()
- val imm = context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
- imm.showSoftInput(binding.title, InputMethodManager.SHOW_IMPLICIT)
- }
- if (!preferences.shownBeastModeHint) {
- showBeastModeHint()
- }
- }
-
- override fun onMenuItemClick(item: MenuItem): Boolean {
- activity?.hideKeyboard()
- if (item.itemId == R.id.menu_delete) {
- deleteButtonClick()
- return true
- } else if (item.itemId == R.id.menu_discard) {
- discardButtonClick()
- return true
- }
- return false
- }
-
suspend fun save(remove: Boolean = true) {
editViewModel.save(remove)
activity?.let { playServices.requestReview(it) }
}
private fun discardButtonClick() {
- if (editViewModel.hasChanges()) {
+ activity?.hideKeyboard()
+ if (editViewModel.hasChanges()) {
dialogBuilder
.newDialog(R.string.discard_confirmation)
.setPositiveButton(R.string.keep_editing, null)
@@ -403,6 +327,7 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
private fun deleteButtonClick() {
+ activity?.hideKeyboard()
dialogBuilder
.newDialog(R.string.DLG_delete_this_task_question)
.setPositiveButton(R.string.ok) { _, _ -> delete() }
@@ -435,6 +360,38 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
}
}
+ @Composable
+ private fun TitleRow(
+ requestFocus: Boolean,
+ ) {
+ val isComplete = editViewModel.completed.collectAsStateWithLifecycle().value
+ val recurrence = editViewModel.recurrence.collectAsStateWithLifecycle().value
+ val isRecurring = remember(recurrence) {
+ !recurrence.isNullOrBlank()
+ }
+ org.tasks.compose.edit.TitleRow(
+ text = editViewModel.title,
+ onChanged = { text -> editViewModel.title = text.toString().trim { it <= ' ' } },
+ linkify = if (preferences.linkify) linkify else null,
+ markdownProvider = markdownProvider,
+ desaturate = remember { preferences.desaturateDarkMode },
+ isCompleted = isComplete,
+ isRecurring = isRecurring,
+ priority = editViewModel.priority.collectAsStateWithLifecycle().value,
+ onComplete = {
+ if (isComplete) {
+ editViewModel.completed.value = false
+ } else {
+ editViewModel.completed.value = true
+ lifecycleScope.launch {
+ save()
+ }
+ }
+ },
+ requestFocus = requestFocus,
+ )
+ }
+
@Composable
private fun DueDateRow() {
val dueDate = editViewModel.dueDate.collectAsStateWithLifecycle().value
diff --git a/app/src/main/java/com/todoroo/astrid/files/FilesControlSet.kt b/app/src/main/java/com/todoroo/astrid/files/FilesControlSet.kt
index dc09c2cb9..d9f43333a 100644
--- a/app/src/main/java/com/todoroo/astrid/files/FilesControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/files/FilesControlSet.kt
@@ -25,7 +25,6 @@ import org.tasks.data.entity.TaskAttachment
import org.tasks.dialogs.AddAttachmentDialog
import org.tasks.files.FileHelper
import org.tasks.preferences.Preferences
-import org.tasks.themes.TasksTheme
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@@ -48,22 +47,20 @@ class FilesControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- AttachmentRow(
- attachments = viewModel.selectedAttachments.collectAsStateWithLifecycle().value,
- openAttachment = {
- FileHelper.startActionView(
- requireActivity(),
- if (Strings.isNullOrEmpty(it.uri)) null else Uri.parse(it.uri)
- )
- },
- deleteAttachment = this@FilesControlSet::deleteAttachment,
- addAttachment = {
- AddAttachmentDialog.newAddAttachmentDialog(this@FilesControlSet)
- .show(parentFragmentManager, FRAG_TAG_ADD_ATTACHMENT_DIALOG)
- },
- )
- }
+ AttachmentRow(
+ attachments = viewModel.selectedAttachments.collectAsStateWithLifecycle().value,
+ openAttachment = {
+ FileHelper.startActionView(
+ requireActivity(),
+ if (Strings.isNullOrEmpty(it.uri)) null else Uri.parse(it.uri)
+ )
+ },
+ deleteAttachment = this@FilesControlSet::deleteAttachment,
+ addAttachment = {
+ AddAttachmentDialog.newAddAttachmentDialog(this@FilesControlSet)
+ .show(parentFragmentManager, FRAG_TAG_ADD_ATTACHMENT_DIALOG)
+ },
+ )
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt
index 41fd58c8b..251476425 100644
--- a/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/repeats/RepeatControlSet.kt
@@ -26,7 +26,6 @@ import org.tasks.filters.GtasksFilter
import org.tasks.repeats.BasicRecurrenceDialog
import org.tasks.repeats.RecurrenceUtils.newRecur
import org.tasks.repeats.RepeatRuleToString
-import org.tasks.themes.TasksTheme
import org.tasks.time.DateTime
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.startOfDay
@@ -86,38 +85,36 @@ class RepeatControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- RepeatRow(
- recurrence = viewModel.recurrence.collectAsStateWithLifecycle().value?.let {
- repeatRuleToString.toString(it)
- },
- repeatAfterCompletion = viewModel.repeatAfterCompletion.collectAsStateWithLifecycle().value,
- onClick = {
- lifecycleScope.launch {
- val accountType = viewModel.selectedList.value
- .let {
- when (it) {
- is CaldavFilter -> it.account
- is GtasksFilter -> it.account
- else -> null
- }
+ RepeatRow(
+ recurrence = viewModel.recurrence.collectAsStateWithLifecycle().value?.let {
+ repeatRuleToString.toString(it)
+ },
+ repeatAfterCompletion = viewModel.repeatAfterCompletion.collectAsStateWithLifecycle().value,
+ onClick = {
+ lifecycleScope.launch {
+ val accountType = viewModel.selectedList.value
+ .let {
+ when (it) {
+ is CaldavFilter -> it.account
+ is GtasksFilter -> it.account
+ else -> null
}
- ?.let { caldavDao.getAccountByUuid(it) }
- ?.accountType
- ?: CaldavAccount.TYPE_LOCAL
- BasicRecurrenceDialog.newBasicRecurrenceDialog(
- target = this@RepeatControlSet,
- rc = REQUEST_RECURRENCE,
- rrule = viewModel.recurrence.value,
- dueDate = viewModel.dueDate.value,
- accountType = accountType,
- )
- .show(parentFragmentManager, FRAG_TAG_BASIC_RECURRENCE)
- }
- },
- onRepeatFromChanged = { viewModel.repeatAfterCompletion.value = it }
- )
- }
+ }
+ ?.let { caldavDao.getAccountByUuid(it) }
+ ?.accountType
+ ?: CaldavAccount.TYPE_LOCAL
+ BasicRecurrenceDialog.newBasicRecurrenceDialog(
+ target = this@RepeatControlSet,
+ rc = REQUEST_RECURRENCE,
+ rrule = viewModel.recurrence.value,
+ dueDate = viewModel.dueDate.value,
+ accountType = accountType,
+ )
+ .show(parentFragmentManager, FRAG_TAG_BASIC_RECURRENCE)
+ }
+ },
+ onRepeatFromChanged = { viewModel.repeatAfterCompletion.value = it }
+ )
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/tags/TagsControlSet.kt b/app/src/main/java/com/todoroo/astrid/tags/TagsControlSet.kt
index 61ffae9b5..6df04aac0 100644
--- a/app/src/main/java/com/todoroo/astrid/tags/TagsControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/tags/TagsControlSet.kt
@@ -29,16 +29,14 @@ class TagsControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- TagsRow(
- tags = viewModel.selectedTags.collectAsStateWithLifecycle().value,
- colorProvider = { chipProvider.getColor(it) },
- onClick = this@TagsControlSet::onRowClick,
- onClear = { tag ->
- viewModel.selectedTags.update { ArrayList(it.minus(tag)) }
- },
- )
- }
+ TagsRow(
+ tags = viewModel.selectedTags.collectAsStateWithLifecycle().value,
+ colorProvider = { chipProvider.getColor(it) },
+ onClick = this@TagsControlSet::onRowClick,
+ onClear = { tag ->
+ viewModel.selectedTags.update { ArrayList(it.minus(tag)) }
+ },
+ )
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt b/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt
index 590ce8d4b..54be380a0 100644
--- a/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/timers/TimerControlSet.kt
@@ -23,7 +23,6 @@ import org.tasks.data.entity.Task
import org.tasks.dialogs.DialogBuilder
import org.tasks.extensions.Context.is24HourFormat
import org.tasks.kmp.org.tasks.time.getTimeString
-import org.tasks.themes.TasksTheme
import org.tasks.themes.Theme
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.ui.TaskEditControlFragment
@@ -90,15 +89,13 @@ class TimerControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- TimerRow(
- started = viewModel.timerStarted.collectAsStateWithLifecycle().value,
- estimated = viewModel.estimatedSeconds.collectAsStateWithLifecycle().value,
- elapsed = viewModel.elapsedSeconds.collectAsStateWithLifecycle().value,
- timerClicked = this@TimerControlSet::timerClicked,
- onClick = this@TimerControlSet::onRowClick,
- )
- }
+ TimerRow(
+ started = viewModel.timerStarted.collectAsStateWithLifecycle().value,
+ estimated = viewModel.estimatedSeconds.collectAsStateWithLifecycle().value,
+ elapsed = viewModel.elapsedSeconds.collectAsStateWithLifecycle().value,
+ timerClicked = this@TimerControlSet::timerClicked,
+ onClick = this@TimerControlSet::onRowClick,
+ )
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt
index e8065b4c4..cf6d0b7ba 100644
--- a/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/ui/ReminderControlSet.kt
@@ -31,7 +31,6 @@ import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.MyTimePickerDialog
import org.tasks.extensions.Context.openReminderSettings
import org.tasks.scheduling.NotificationSchedulerIntentService
-import org.tasks.themes.TasksTheme
import org.tasks.ui.TaskEditControlFragment
import javax.inject.Inject
@@ -76,57 +75,55 @@ class ReminderControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- val ringMode by remember { this@ReminderControlSet.ringMode }
- val hasReminderPermissions by rememberReminderPermissionState()
- val notificationPermissions = if (AndroidUtilities.atLeastTiramisu()) {
- rememberPermissionState(
- Manifest.permission.POST_NOTIFICATIONS,
- onPermissionResult = { success ->
- if (success) {
- NotificationSchedulerIntentService.enqueueWork(context)
- }
+ val ringMode by remember { this@ReminderControlSet.ringMode }
+ val hasReminderPermissions by rememberReminderPermissionState()
+ val notificationPermissions = if (AndroidUtilities.atLeastTiramisu()) {
+ rememberPermissionState(
+ Manifest.permission.POST_NOTIFICATIONS,
+ onPermissionResult = { success ->
+ if (success) {
+ NotificationSchedulerIntentService.enqueueWork(context)
}
- )
- } else {
- null
- }
- val pickDateAndTime =
- rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
- if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
- val data = result.data ?: return@rememberLauncherForActivityResult
- val timestamp =
- data.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, 0L)
- val replace: Alarm? = data.getParcelableExtra(EXTRA_REPLACE)
- replace?.let { viewModel.removeAlarm(it) }
- viewModel.addAlarm(Alarm(time = timestamp, type = TYPE_DATE_TIME))
- }
- AlarmRow(
- alarms = viewModel.selectedAlarms.collectAsStateWithLifecycle().value,
- hasNotificationPermissions = hasReminderPermissions &&
- (notificationPermissions == null || notificationPermissions.status == PermissionStatus.Granted),
- fixNotificationPermissions = {
- if (hasReminderPermissions) {
- notificationPermissions?.launchPermissionRequest()
- } else {
- context.openReminderSettings()
- }
- },
- ringMode = ringMode,
- addAlarm = viewModel::addAlarm,
- openRingType = this@ReminderControlSet::onClickRingType,
- deleteAlarm = viewModel::removeAlarm,
- pickDateAndTime = { replace ->
- val timestamp = replace?.takeIf { it.type == TYPE_DATE_TIME }?.time
- ?: DateTimeUtils.newDateTime().noon().millis
- pickDateAndTime.launch(
- Intent(activity, DateAndTimePickerActivity::class.java)
- .putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, timestamp)
- .putExtra(EXTRA_REPLACE, replace)
- )
}
)
+ } else {
+ null
}
+ val pickDateAndTime =
+ rememberLauncherForActivityResult(ActivityResultContracts.StartActivityForResult()) { result ->
+ if (result.resultCode != RESULT_OK) return@rememberLauncherForActivityResult
+ val data = result.data ?: return@rememberLauncherForActivityResult
+ val timestamp =
+ data.getLongExtra(MyTimePickerDialog.EXTRA_TIMESTAMP, 0L)
+ val replace: Alarm? = data.getParcelableExtra(EXTRA_REPLACE)
+ replace?.let { viewModel.removeAlarm(it) }
+ viewModel.addAlarm(Alarm(time = timestamp, type = TYPE_DATE_TIME))
+ }
+ AlarmRow(
+ alarms = viewModel.selectedAlarms.collectAsStateWithLifecycle().value,
+ hasNotificationPermissions = hasReminderPermissions &&
+ (notificationPermissions == null || notificationPermissions.status == PermissionStatus.Granted),
+ fixNotificationPermissions = {
+ if (hasReminderPermissions) {
+ notificationPermissions?.launchPermissionRequest()
+ } else {
+ context.openReminderSettings()
+ }
+ },
+ ringMode = ringMode,
+ addAlarm = viewModel::addAlarm,
+ openRingType = this@ReminderControlSet::onClickRingType,
+ deleteAlarm = viewModel::removeAlarm,
+ pickDateAndTime = { replace ->
+ val timestamp = replace?.takeIf { it.type == TYPE_DATE_TIME }?.time
+ ?: DateTimeUtils.newDateTime().noon().millis
+ pickDateAndTime.launch(
+ Intent(activity, DateAndTimePickerActivity::class.java)
+ .putExtra(DateAndTimePickerActivity.EXTRA_TIMESTAMP, timestamp)
+ .putExtra(EXTRA_REPLACE, replace)
+ )
+ }
+ )
}
}
diff --git a/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt b/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt
index 64a6f1ad6..441b723d0 100644
--- a/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt
+++ b/app/src/main/java/com/todoroo/astrid/ui/StartDateControlSet.kt
@@ -23,7 +23,6 @@ import org.tasks.kmp.org.tasks.time.DateStyle
import org.tasks.kmp.org.tasks.time.getRelativeDateTime
import org.tasks.kmp.org.tasks.time.getTimeString
import org.tasks.preferences.Preferences
-import org.tasks.themes.TasksTheme
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import org.tasks.time.withMillisOfDay
import org.tasks.ui.TaskEditControlFragment
@@ -49,42 +48,40 @@ class StartDateControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?) =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- val selectedDay = vm.selectedDay.collectAsStateWithLifecycle().value
- val selectedTime = vm.selectedTime.collectAsStateWithLifecycle().value
- StartDateRow(
- startDate = viewModel.startDate.collectAsStateWithLifecycle().value,
- selectedDay = selectedDay,
- selectedTime = selectedTime,
- hasDueDate = viewModel.dueDate.collectAsStateWithLifecycle().value > 0,
- printDate = {
- runBlocking {
- getRelativeDateTime(
- selectedDay + selectedTime,
- requireContext().is24HourFormat,
- DateStyle.FULL,
- alwaysDisplayFullDate = preferences.alwaysDisplayFullDate
- )
- }
- },
- onClick = {
- val fragmentManager = parentFragmentManager
- if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) {
- StartDatePicker.newDateTimePicker(
- this@StartDateControlSet,
- REQUEST_START_DATE,
- vm.selectedDay.value,
- vm.selectedTime.value,
- preferences.getBoolean(
- R.string.p_auto_dismiss_datetime_edit_screen,
- false
- )
+ val selectedDay = vm.selectedDay.collectAsStateWithLifecycle().value
+ val selectedTime = vm.selectedTime.collectAsStateWithLifecycle().value
+ StartDateRow(
+ startDate = viewModel.startDate.collectAsStateWithLifecycle().value,
+ selectedDay = selectedDay,
+ selectedTime = selectedTime,
+ hasDueDate = viewModel.dueDate.collectAsStateWithLifecycle().value > 0,
+ printDate = {
+ runBlocking {
+ getRelativeDateTime(
+ selectedDay + selectedTime,
+ requireContext().is24HourFormat,
+ DateStyle.FULL,
+ alwaysDisplayFullDate = preferences.alwaysDisplayFullDate
+ )
+ }
+ },
+ onClick = {
+ val fragmentManager = parentFragmentManager
+ if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) {
+ StartDatePicker.newDateTimePicker(
+ this@StartDateControlSet,
+ REQUEST_START_DATE,
+ vm.selectedDay.value,
+ vm.selectedTime.value,
+ preferences.getBoolean(
+ R.string.p_auto_dismiss_datetime_edit_screen,
+ false
)
- .show(fragmentManager, FRAG_TAG_DATE_PICKER)
- }
+ )
+ .show(fragmentManager, FRAG_TAG_DATE_PICKER)
}
- )
- }
+ }
+ )
}
}
diff --git a/app/src/main/java/org/tasks/compose/CheckBox.kt b/app/src/main/java/org/tasks/compose/CheckBox.kt
index 889e4562d..8279308fd 100644
--- a/app/src/main/java/org/tasks/compose/CheckBox.kt
+++ b/app/src/main/java/org/tasks/compose/CheckBox.kt
@@ -17,13 +17,32 @@ fun CheckBox(
onCompleteClick: () -> Unit,
modifier: Modifier = Modifier,
desaturate: Boolean,
+) {
+ CheckBox(
+ isCompleted = task.isCompleted,
+ isRecurring = task.isRecurring,
+ priority = task.priority,
+ onCompleteClick = onCompleteClick,
+ modifier = modifier,
+ desaturate = desaturate,
+ )
+}
+
+@Composable
+fun CheckBox(
+ isCompleted: Boolean,
+ isRecurring: Boolean,
+ priority: Int,
+ onCompleteClick: () -> Unit,
+ modifier: Modifier = Modifier,
+ desaturate: Boolean,
) {
IconButton(onClick = onCompleteClick, modifier = modifier) {
Icon(
- painter = painterResource(id = task.getCheckboxRes()),
+ painter = painterResource(id = getCheckboxRes(isCompleted, isRecurring)),
tint = Color(
priorityColor(
- priority = task.priority,
+ priority = priority,
isDarkMode = isSystemInDarkTheme(),
desaturate = desaturate,
)
@@ -31,4 +50,4 @@ fun CheckBox(
contentDescription = null,
)
}
-}
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/compose/TaskEditIcon.kt b/app/src/main/java/org/tasks/compose/TaskEditIcon.kt
index 94f24b012..5983b94b1 100644
--- a/app/src/main/java/org/tasks/compose/TaskEditIcon.kt
+++ b/app/src/main/java/org/tasks/compose/TaskEditIcon.kt
@@ -1,6 +1,8 @@
package org.tasks.compose
import androidx.annotation.DrawableRes
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.size
import androidx.compose.material.ContentAlpha
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
@@ -8,13 +10,17 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.res.painterResource
+import androidx.compose.ui.unit.dp
@Composable
fun TaskEditIcon(@DrawableRes id: Int, modifier: Modifier = Modifier) {
Icon(
painter = painterResource(id = id),
contentDescription = null,
- modifier = modifier.alpha(ContentAlpha.medium),
+ modifier = modifier
+ .alpha(ContentAlpha.medium)
+ .padding(12.dp)
+ .size(24.dp),
tint = MaterialTheme.colorScheme.onSurface,
)
}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/compose/TaskEditRow.kt b/app/src/main/java/org/tasks/compose/TaskEditRow.kt
index c28339a4b..a0816a805 100644
--- a/app/src/main/java/org/tasks/compose/TaskEditRow.kt
+++ b/app/src/main/java/org/tasks/compose/TaskEditRow.kt
@@ -1,10 +1,12 @@
package org.tasks.compose
+import androidx.compose.foundation.background
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.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.alpha
@@ -19,10 +21,10 @@ fun TaskEditRow(
modifier = Modifier
.alpha(ContentAlpha.medium)
.padding(
- start = 16.dp,
- top = 20.dp,
- end = 32.dp,
- bottom = 20.dp
+ start = 4.dp,
+ top = 8.dp,
+ end = 20.dp,
+ bottom = 8.dp
)
)
},
@@ -35,6 +37,7 @@ fun TaskEditRow(
enabled = onClick != null,
onClick = { onClick?.invoke() }
)
+ .background(MaterialTheme.colorScheme.surface),
) {
icon()
content()
diff --git a/app/src/main/java/org/tasks/compose/edit/DescriptionRow.kt b/app/src/main/java/org/tasks/compose/edit/DescriptionRow.kt
index 5b38d5afb..311e8490f 100644
--- a/app/src/main/java/org/tasks/compose/edit/DescriptionRow.kt
+++ b/app/src/main/java/org/tasks/compose/edit/DescriptionRow.kt
@@ -1,20 +1,16 @@
package org.tasks.compose.edit
import android.content.res.Configuration
-import android.text.InputType
-import android.util.TypedValue
-import android.view.View
-import android.view.inputmethod.EditorInfo
-import android.widget.EditText
-import androidx.compose.foundation.layout.*
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
import androidx.compose.runtime.Composable
import androidx.compose.ui.ExperimentalComposeUiApi
import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
-import androidx.compose.ui.viewinterop.AndroidView
-import androidx.core.widget.addTextChangedListener
-import com.todoroo.andlib.utility.AndroidUtilities
import org.tasks.R
import org.tasks.compose.TaskEditRow
import org.tasks.dialogs.Linkify
@@ -33,46 +29,12 @@ fun DescriptionRow(
content = {
Column(verticalArrangement = Arrangement.Center) {
Spacer(modifier = Modifier.height(11.dp))
- AndroidView(
- modifier = Modifier
- .fillMaxWidth()
- .wrapContentHeight()
- .padding(end = 16.dp),
- factory = { context ->
- EditText(context).apply {
- setText(text)
- val textWatcher =
- markdownProvider?.markdown(linkify != null)?.textWatcher(this)
- addTextChangedListener(
- onTextChanged = { text, _, _, _ -> onChanged(text) },
- afterTextChanged = { editable -> textWatcher?.invoke(editable) }
- )
- setBackgroundColor(context.getColor(android.R.color.transparent))
- textAlignment = View.TEXT_ALIGNMENT_VIEW_START
- imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
- inputType =
- InputType.TYPE_CLASS_TEXT or
- InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or
- InputType.TYPE_TEXT_FLAG_MULTI_LINE or
- InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
- isSingleLine = false
- maxLines = Int.MAX_VALUE
- if (AndroidUtilities.atLeastOreo()) {
- importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
- }
- isVerticalScrollBarEnabled = true
- freezesText = true
- setHorizontallyScrolling(false)
- isHorizontalScrollBarEnabled = false
- setHint(R.string.TEA_note_label)
- setHintTextColor(context.getColor(R.color.text_tertiary))
- setTextSize(
- TypedValue.COMPLEX_UNIT_PX,
- context.resources.getDimension(R.dimen.task_edit_text_size)
- )
- linkify?.linkify(this)
- }
- },
+ EditTextView(
+ text = text,
+ hint = stringResource(R.string.TEA_note_label),
+ onChanged = onChanged,
+ linkify = linkify,
+ markdownProvider = markdownProvider,
)
Spacer(modifier = Modifier.height(11.dp))
}
diff --git a/app/src/main/java/org/tasks/compose/edit/EditTextView.kt b/app/src/main/java/org/tasks/compose/edit/EditTextView.kt
new file mode 100644
index 000000000..6f7030161
--- /dev/null
+++ b/app/src/main/java/org/tasks/compose/edit/EditTextView.kt
@@ -0,0 +1,106 @@
+package org.tasks.compose.edit
+
+import android.content.Context.INPUT_METHOD_SERVICE
+import android.graphics.Paint
+import android.text.InputType
+import android.util.TypedValue
+import android.view.View
+import android.view.inputmethod.EditorInfo
+import android.view.inputmethod.InputMethodManager
+import android.widget.EditText
+import androidx.compose.foundation.layout.fillMaxWidth
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.layout.wrapContentHeight
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.LaunchedEffect
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.mutableStateOf
+import androidx.compose.runtime.remember
+import androidx.compose.runtime.setValue
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.focus.FocusRequester
+import androidx.compose.ui.focus.focusRequester
+import androidx.compose.ui.platform.LocalContext
+import androidx.compose.ui.unit.dp
+import androidx.compose.ui.viewinterop.AndroidView
+import androidx.core.widget.addTextChangedListener
+import com.todoroo.andlib.utility.AndroidUtilities
+import org.tasks.R
+import org.tasks.dialogs.Linkify
+import org.tasks.markdown.MarkdownProvider
+
+@Composable
+fun EditTextView(
+ text: String?,
+ hint: String?,
+ onChanged: (CharSequence?) -> Unit,
+ linkify: Linkify?,
+ markdownProvider: MarkdownProvider?,
+ strikethrough: Boolean = false,
+ requestFocus: Boolean = false,
+) {
+ val context = LocalContext.current
+ var shouldRequestFocus by remember { mutableStateOf(false) }
+ val focusRequester = remember { FocusRequester() }
+ AndroidView(
+ modifier = Modifier
+ .fillMaxWidth()
+ .wrapContentHeight()
+ .padding(end = 16.dp)
+ .focusRequester(focusRequester),
+ factory = { context ->
+ EditText(context).apply {
+ setText(text)
+ val textWatcher =
+ markdownProvider?.markdown(linkify != null)?.textWatcher(this)
+ addTextChangedListener(
+ onTextChanged = { text, _, _, _ -> onChanged(text) },
+ afterTextChanged = { editable -> textWatcher?.invoke(editable) }
+ )
+ setBackgroundColor(context.getColor(android.R.color.transparent))
+ textAlignment = View.TEXT_ALIGNMENT_VIEW_START
+ imeOptions = EditorInfo.IME_FLAG_NO_EXTRACT_UI
+ inputType =
+ InputType.TYPE_CLASS_TEXT or
+ InputType.TYPE_TEXT_FLAG_CAP_SENTENCES or
+ InputType.TYPE_TEXT_FLAG_MULTI_LINE or
+ InputType.TYPE_TEXT_FLAG_AUTO_CORRECT
+ isSingleLine = false
+ maxLines = Int.MAX_VALUE
+ if (AndroidUtilities.atLeastOreo()) {
+ importantForAutofill = View.IMPORTANT_FOR_AUTOFILL_NO
+ }
+ isVerticalScrollBarEnabled = true
+ freezesText = true
+ setHorizontallyScrolling(false)
+ isHorizontalScrollBarEnabled = false
+ setHint(hint)
+ setHintTextColor(context.getColor(R.color.text_tertiary))
+ setTextSize(
+ TypedValue.COMPLEX_UNIT_PX,
+ context.resources.getDimension(R.dimen.task_edit_text_size)
+ )
+ linkify?.linkify(this)
+ }
+ },
+ update = { view ->
+ if (shouldRequestFocus) {
+ view.requestFocus()
+ val imm = context.getSystemService(INPUT_METHOD_SERVICE) as InputMethodManager
+ imm.showSoftInput(view, InputMethodManager.SHOW_IMPLICIT)
+ shouldRequestFocus = false
+ }
+ view.paintFlags = if (strikethrough) {
+ view.paintFlags or Paint.STRIKE_THRU_TEXT_FLAG
+ } else {
+ view.paintFlags and Paint.STRIKE_THRU_TEXT_FLAG.inv()
+ }
+ },
+ )
+
+ LaunchedEffect(Unit) {
+ if (requestFocus) {
+ shouldRequestFocus = true
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt b/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt
index 78c782264..7e74996c6 100644
--- a/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt
+++ b/app/src/main/java/org/tasks/compose/edit/SubtaskRow.kt
@@ -70,10 +70,10 @@ fun SubtaskRow(
id = org.tasks.R.drawable.ic_subdirectory_arrow_right_black_24dp,
modifier = Modifier
.padding(
- start = 16.dp,
- top = 20.dp,
- end = 20.dp,
- bottom = 20.dp
+ start = 4.dp,
+ top = 8.dp,
+ end = 8.dp,
+ bottom = 8.dp
)
.alpha(ContentAlpha.medium),
)
diff --git a/app/src/main/java/org/tasks/compose/edit/TitleRow.kt b/app/src/main/java/org/tasks/compose/edit/TitleRow.kt
new file mode 100644
index 000000000..b091de3ab
--- /dev/null
+++ b/app/src/main/java/org/tasks/compose/edit/TitleRow.kt
@@ -0,0 +1,111 @@
+package org.tasks.compose.edit
+
+import android.content.res.Configuration
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.ExperimentalComposeUiApi
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.tooling.preview.Preview
+import androidx.compose.ui.unit.dp
+import org.tasks.R
+import org.tasks.compose.CheckBox
+import org.tasks.compose.TaskEditRow
+import org.tasks.dialogs.Linkify
+import org.tasks.markdown.MarkdownProvider
+import org.tasks.themes.TasksTheme
+
+@Composable
+fun TitleRow(
+ text: String?,
+ onChanged: (CharSequence?) -> Unit,
+ linkify: Linkify?,
+ markdownProvider: MarkdownProvider?,
+ desaturate: Boolean,
+ isCompleted: Boolean,
+ isRecurring: Boolean,
+ priority: Int,
+ onComplete: () -> Unit,
+ requestFocus: Boolean,
+) {
+ TaskEditRow(
+ icon = {
+ CheckBox(
+ isCompleted = isCompleted,
+ isRecurring = isRecurring,
+ priority = priority,
+ onCompleteClick = onComplete,
+ desaturate = desaturate,
+ modifier = Modifier.padding(
+ start = 4.dp,
+ end = 20.dp,
+ )
+ )
+ },
+ content = {
+ Column(verticalArrangement = Arrangement.Center) {
+ Spacer(modifier = Modifier.height(3.dp))
+ EditTextView(
+ text = text,
+ hint = stringResource(R.string.TEA_title_hint),
+ onChanged = onChanged,
+ linkify = linkify,
+ markdownProvider = markdownProvider,
+ strikethrough = isCompleted,
+ requestFocus = requestFocus,
+ )
+ Spacer(modifier = Modifier.height(11.dp))
+ }
+ },
+ )
+}
+
+@ExperimentalComposeUiApi
+@Preview(showBackground = true, widthDp = 320)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
+@Composable
+fun EmptyTitlePreview() {
+ TasksTheme {
+ TitleRow(
+ text = null,
+ onChanged = {},
+ linkify = null,
+ markdownProvider = null,
+ desaturate = true,
+ isCompleted = false,
+ isRecurring = false,
+ priority = 0,
+ onComplete = {},
+ requestFocus = false,
+ )
+ }
+}
+
+@ExperimentalComposeUiApi
+@Preview(showBackground = true, widthDp = 320)
+@Preview(showBackground = true, uiMode = Configuration.UI_MODE_NIGHT_YES, widthDp = 320)
+@Composable
+fun TitlePreview() {
+ TasksTheme {
+ TitleRow(
+ text = """
+ Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
+
+ Eleifend quam adipiscing vitae proin sagittis. Faucibus a pellentesque sit amet porttitor eget dolor.
+ """.trimIndent(),
+ onChanged = {},
+ linkify = null,
+ markdownProvider = null,
+ desaturate = true,
+ isCompleted = false,
+ isRecurring = false,
+ priority = 0,
+ onComplete = {},
+ requestFocus = false,
+ )
+ }
+}
diff --git a/app/src/main/java/org/tasks/ui/CalendarControlSet.kt b/app/src/main/java/org/tasks/ui/CalendarControlSet.kt
index e83b07676..d79079ea7 100644
--- a/app/src/main/java/org/tasks/ui/CalendarControlSet.kt
+++ b/app/src/main/java/org/tasks/ui/CalendarControlSet.kt
@@ -18,7 +18,6 @@ import org.tasks.calendars.CalendarProvider
import org.tasks.compose.edit.CalendarRow
import org.tasks.extensions.Context.toast
import org.tasks.preferences.PermissionChecker
-import org.tasks.themes.TasksTheme
import timber.log.Timber
import javax.inject.Inject
@@ -45,34 +44,32 @@ class CalendarControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- CalendarRow(
- eventUri = viewModel.eventUri.collectAsStateWithLifecycle().value,
- selectedCalendar = viewModel.selectedCalendar.collectAsStateWithLifecycle().value?.let {
- calendarProvider.getCalendar(it)?.name
- },
- onClick = {
- if (viewModel.eventUri.value.isNullOrBlank()) {
- CalendarPicker
- .newCalendarPicker(
- requireParentFragment(),
- TaskEditFragment.REQUEST_CODE_PICK_CALENDAR,
- viewModel.selectedCalendar.value,
- )
- .show(
- requireParentFragment().parentFragmentManager,
- TaskEditFragment.FRAG_TAG_CALENDAR_PICKER
- )
- } else {
- openCalendarEvent()
- }
- },
- clear = {
- viewModel.selectedCalendar.value = null
- viewModel.eventUri.value = null
+ CalendarRow(
+ eventUri = viewModel.eventUri.collectAsStateWithLifecycle().value,
+ selectedCalendar = viewModel.selectedCalendar.collectAsStateWithLifecycle().value?.let {
+ calendarProvider.getCalendar(it)?.name
+ },
+ onClick = {
+ if (viewModel.eventUri.value.isNullOrBlank()) {
+ CalendarPicker
+ .newCalendarPicker(
+ requireParentFragment(),
+ TaskEditFragment.REQUEST_CODE_PICK_CALENDAR,
+ viewModel.selectedCalendar.value,
+ )
+ .show(
+ requireParentFragment().parentFragmentManager,
+ TaskEditFragment.FRAG_TAG_CALENDAR_PICKER
+ )
+ } else {
+ openCalendarEvent()
}
- )
- }
+ },
+ clear = {
+ viewModel.selectedCalendar.value = null
+ viewModel.eventUri.value = null
+ }
+ )
}
}
diff --git a/app/src/main/java/org/tasks/ui/CheckBoxProvider.kt b/app/src/main/java/org/tasks/ui/CheckBoxProvider.kt
index 132f9ae85..eca4116d1 100644
--- a/app/src/main/java/org/tasks/ui/CheckBoxProvider.kt
+++ b/app/src/main/java/org/tasks/ui/CheckBoxProvider.kt
@@ -4,9 +4,9 @@ import android.content.Context
import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes
import androidx.appcompat.content.res.AppCompatResources
-import org.tasks.data.entity.Task
import dagger.hilt.android.qualifiers.ActivityContext
import org.tasks.R
+import org.tasks.data.entity.Task
import org.tasks.themes.ColorProvider
import javax.inject.Inject
@@ -24,7 +24,9 @@ class CheckBoxProvider @Inject constructor(
}
companion object {
- fun Task.getCheckboxRes() = when {
+ fun Task.getCheckboxRes() = getCheckboxRes(isCompleted, isRecurring)
+
+ fun getCheckboxRes(isCompleted: Boolean, isRecurring: Boolean) = when {
isCompleted -> R.drawable.ic_outline_check_box_24px
isRecurring -> R.drawable.ic_outline_repeat_24px
else -> R.drawable.ic_outline_check_box_outline_blank_24px
diff --git a/app/src/main/java/org/tasks/ui/LocationControlSet.kt b/app/src/main/java/org/tasks/ui/LocationControlSet.kt
index 3cd278873..d9643dea9 100644
--- a/app/src/main/java/org/tasks/ui/LocationControlSet.kt
+++ b/app/src/main/java/org/tasks/ui/LocationControlSet.kt
@@ -29,7 +29,6 @@ import org.tasks.location.LocationPickerActivity
import org.tasks.preferences.PermissionChecker
import org.tasks.preferences.PermissionChecker.backgroundPermissions
import org.tasks.preferences.Preferences
-import org.tasks.themes.TasksTheme
import javax.inject.Inject
@AndroidEntryPoint
@@ -85,27 +84,25 @@ class LocationControlSet : TaskEditControlFragment() {
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
- TasksTheme {
- val hasPermissions =
- rememberMultiplePermissionsState(permissions = backgroundPermissions())
- .allPermissionsGranted
- LocationRow(
- location = viewModel.selectedLocation.collectAsStateWithLifecycle().value,
- hasPermissions = hasPermissions,
- onClick = this@LocationControlSet::onRowClick,
- openGeofenceOptions = {
- if (hasPermissions) {
- showGeofenceOptions()
- } else {
- newLocationPermissionDialog(
- this@LocationControlSet,
- REQUEST_LOCATION_PERMISSIONS
- )
- .show(parentFragmentManager, FRAG_TAG_REQUEST_LOCATION)
- }
+ val hasPermissions =
+ rememberMultiplePermissionsState(permissions = backgroundPermissions())
+ .allPermissionsGranted
+ LocationRow(
+ location = viewModel.selectedLocation.collectAsStateWithLifecycle().value,
+ hasPermissions = hasPermissions,
+ onClick = this@LocationControlSet::onRowClick,
+ openGeofenceOptions = {
+ if (hasPermissions) {
+ showGeofenceOptions()
+ } else {
+ newLocationPermissionDialog(
+ this@LocationControlSet,
+ REQUEST_LOCATION_PERMISSIONS
+ )
+ .show(parentFragmentManager, FRAG_TAG_REQUEST_LOCATION)
}
- )
- }
+ }
+ )
}
}
diff --git a/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt b/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt
index fff74920f..06b895447 100644
--- a/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt
+++ b/app/src/main/java/org/tasks/ui/SubtaskControlSet.kt
@@ -25,7 +25,6 @@ import org.tasks.preferences.Preferences
import org.tasks.tasklist.SectionedDataSource
import org.tasks.tasklist.TasksResults
import org.tasks.themes.ColorProvider
-import org.tasks.themes.TasksTheme
import org.tasks.time.DateTimeUtils2.currentTimeMillis
import javax.inject.Inject
@@ -55,39 +54,37 @@ class SubtaskControlSet : TaskEditControlFragment() {
(parent as ComposeView).apply {
listViewModel = ViewModelProvider(requireParentFragment())[TaskListViewModel::class.java]
setContent {
- TasksTheme {
- SubtaskRow(
- originalFilter = viewModel.originalList,
- filter = viewModel.selectedList.collectAsStateWithLifecycle().value,
- hasParent = viewModel.hasParent,
- desaturate = preferences.desaturateDarkMode,
- existingSubtasks = if (viewModel.isNew) {
- TasksResults.Results(SectionedDataSource())
- } else {
- listViewModel.state.collectAsStateWithLifecycle().value.tasks
- },
- newSubtasks = viewModel.newSubtasks.collectAsStateWithLifecycle().value,
- openSubtask = this@SubtaskControlSet::openSubtask,
- completeExistingSubtask = this@SubtaskControlSet::complete,
- toggleSubtask = this@SubtaskControlSet::toggleSubtask,
- addSubtask = this@SubtaskControlSet::addSubtask,
- completeNewSubtask = {
- viewModel.newSubtasks.value =
- ArrayList(viewModel.newSubtasks.value).apply {
- val modified = it.copy(
- completionDate = if (it.isCompleted) 0 else currentTimeMillis()
- )
- set(indexOf(it), modified)
- }
- },
- deleteSubtask = {
- viewModel.newSubtasks.value =
- ArrayList(viewModel.newSubtasks.value).apply {
- remove(it)
- }
- }
- )
- }
+ SubtaskRow(
+ originalFilter = viewModel.originalList,
+ filter = viewModel.selectedList.collectAsStateWithLifecycle().value,
+ hasParent = viewModel.hasParent,
+ desaturate = preferences.desaturateDarkMode,
+ existingSubtasks = if (viewModel.isNew) {
+ TasksResults.Results(SectionedDataSource())
+ } else {
+ listViewModel.state.collectAsStateWithLifecycle().value.tasks
+ },
+ newSubtasks = viewModel.newSubtasks.collectAsStateWithLifecycle().value,
+ openSubtask = this@SubtaskControlSet::openSubtask,
+ completeExistingSubtask = this@SubtaskControlSet::complete,
+ toggleSubtask = this@SubtaskControlSet::toggleSubtask,
+ addSubtask = this@SubtaskControlSet::addSubtask,
+ completeNewSubtask = {
+ viewModel.newSubtasks.value =
+ ArrayList(viewModel.newSubtasks.value).apply {
+ val modified = it.copy(
+ completionDate = if (it.isCompleted) 0 else currentTimeMillis()
+ )
+ set(indexOf(it), modified)
+ }
+ },
+ deleteSubtask = {
+ viewModel.newSubtasks.value =
+ ArrayList(viewModel.newSubtasks.value).apply {
+ remove(it)
+ }
+ }
+ )
}
}
diff --git a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt
index 2feb6df1f..e94ea58aa 100644
--- a/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt
+++ b/app/src/main/java/org/tasks/ui/TaskEditViewModel.kt
@@ -102,11 +102,12 @@ class TaskEditViewModel @Inject constructor(
val isNew = task.isNew
+ var showBeastModeHint = MutableStateFlow(!preferences.shownBeastModeHint)
var creationDate: Long = task.creationDate
var modificationDate: Long = task.modificationDate
var completionDate: Long = task.completionDate
var title: String? = task.title
- var completed: Boolean = task.isCompleted
+ var completed = MutableStateFlow(task.isCompleted)
var priority = MutableStateFlow(task.priority)
var description: String? = task.notes.stripCarriageReturns()
val recurrence = MutableStateFlow(task.recurrence)
@@ -217,7 +218,7 @@ class TaskEditViewModel @Inject constructor(
fun hasChanges(): Boolean =
(task.title != title || (isNew && title?.isNotBlank() == true)) ||
- task.isCompleted != completed ||
+ task.isCompleted != completed.value ||
task.dueDate != dueDate.value ||
task.priority != priority.value ||
if (task.notes.isNullOrBlank()) {
@@ -399,10 +400,10 @@ class TaskEditViewModel @Inject constructor(
.let { taskAttachmentDao.insert(it) }
}
- if (task.isCompleted != completed) {
- taskCompleter.setComplete(task, completed)
+ if (task.isCompleted != completed.value) {
+ taskCompleter.setComplete(task, completed.value)
if (task.isCompleted) {
- firebase?.completeTask("edit_screen")
+ firebase?.completeTask("edit_screen_v2")
}
}
@@ -503,6 +504,12 @@ class TaskEditViewModel @Inject constructor(
}
}
+ fun hideBeastModeHint(click: Boolean) {
+ showBeastModeHint.value = false
+ preferences.shownBeastModeHint = true
+ firebase?.logEvent(R.string.event_banner_beast, R.string.param_click to click)
+ }
+
init {
viewModelScope.launch {
taskAttachmentDao.getAttachments(task.id).let { attachments ->
diff --git a/app/src/main/res/layout/fragment_task_edit.xml b/app/src/main/res/layout/fragment_task_edit.xml
deleted file mode 100644
index fb993cfd0..000000000
--- a/app/src/main/res/layout/fragment_task_edit.xml
+++ /dev/null
@@ -1,108 +0,0 @@
-
-