Embed task edit row fragments in compose

pull/1952/head
Alex Baker 3 years ago
parent 22de530590
commit d0fcca1a1f

@ -232,6 +232,7 @@ dependencies {
implementation("androidx.activity:activity-compose:1.4.0") implementation("androidx.activity:activity-compose:1.4.0")
implementation("androidx.compose.material:material-icons-extended:${Versions.compose}") implementation("androidx.compose.material:material-icons-extended:${Versions.compose}")
implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1") implementation("androidx.lifecycle:lifecycle-viewmodel-compose:2.4.1")
implementation("androidx.compose.ui:ui-viewbinding:${Versions.compose}")
releaseCompileOnly("androidx.compose.ui:ui-tooling:${Versions.compose}") releaseCompileOnly("androidx.compose.ui:ui-tooling:${Versions.compose}")
implementation("com.google.accompanist:accompanist-flowlayout:${Versions.accompanist}") implementation("com.google.accompanist:accompanist-flowlayout:${Versions.accompanist}")

@ -21,11 +21,17 @@ import android.view.inputmethod.InputMethodManager
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.appcompat.widget.Toolbar import androidx.appcompat.widget.Toolbar
import androidx.compose.animation.ExperimentalAnimationApi import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.material.Divider
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.viewinterop.AndroidViewBinding
import androidx.coordinatorlayout.widget.CoordinatorLayout import androidx.coordinatorlayout.widget.CoordinatorLayout
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
@ -36,11 +42,19 @@ import com.google.android.material.appbar.AppBarLayout.Behavior.DragCallback
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.AndroidUtilities import com.todoroo.andlib.utility.AndroidUtilities
import com.todoroo.andlib.utility.DateUtilities import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.GtasksFilter
import com.todoroo.astrid.dao.TaskDao import com.todoroo.astrid.dao.TaskDao
import com.todoroo.astrid.data.Task import com.todoroo.astrid.data.Task
import com.todoroo.astrid.files.FilesControlSet
import com.todoroo.astrid.notes.CommentsController import com.todoroo.astrid.notes.CommentsController
import com.todoroo.astrid.repeats.RepeatControlSet
import com.todoroo.astrid.tags.TagsControlSet
import com.todoroo.astrid.timers.TimerControlSet
import com.todoroo.astrid.timers.TimerPlugin import com.todoroo.astrid.timers.TimerPlugin
import com.todoroo.astrid.ui.ReminderControlSet
import com.todoroo.astrid.ui.StartDateControlSet
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@ -50,23 +64,32 @@ import kotlinx.coroutines.withContext
import org.tasks.LocalBroadcastManager import org.tasks.LocalBroadcastManager
import org.tasks.R import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
import org.tasks.activities.ListPicker
import org.tasks.analytics.Firebase import org.tasks.analytics.Firebase
import org.tasks.calendars.CalendarPicker
import org.tasks.compose.BeastModeBanner import org.tasks.compose.BeastModeBanner
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.*
import org.tasks.data.* import org.tasks.data.*
import org.tasks.databinding.FragmentTaskEditBinding import org.tasks.databinding.*
import org.tasks.date.DateTimeUtils.newDateTime import org.tasks.date.DateTimeUtils.newDateTime
import org.tasks.dialogs.DateTimePicker
import org.tasks.dialogs.DialogBuilder import org.tasks.dialogs.DialogBuilder
import org.tasks.dialogs.Linkify import org.tasks.dialogs.Linkify
import org.tasks.files.FileHelper import org.tasks.files.FileHelper
import org.tasks.fragments.CommentBarFragment
import org.tasks.fragments.TaskEditControlSetFragmentManager import org.tasks.fragments.TaskEditControlSetFragmentManager
import org.tasks.fragments.TaskEditControlSetFragmentManager.Companion.TAG_CREATION
import org.tasks.fragments.TaskEditControlSetFragmentManager.Companion.TAG_DESCRIPTION
import org.tasks.fragments.TaskEditControlSetFragmentManager.Companion.TAG_DUE_DATE
import org.tasks.fragments.TaskEditControlSetFragmentManager.Companion.TAG_LIST
import org.tasks.fragments.TaskEditControlSetFragmentManager.Companion.TAG_PRIORITY
import org.tasks.markdown.MarkdownProvider import org.tasks.markdown.MarkdownProvider
import org.tasks.notifications.NotificationManager import org.tasks.notifications.NotificationManager
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.ui.TaskEditEvent import org.tasks.ui.*
import org.tasks.ui.TaskEditEventBus import org.tasks.ui.TaskEditViewModel.Companion.stripCarriageReturns
import org.tasks.ui.TaskEditViewModel import java.time.format.FormatStyle
import org.tasks.ui.TaskListViewModel import java.util.*
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.abs import kotlin.math.abs
@ -86,6 +109,8 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
@Inject lateinit var markdownProvider: MarkdownProvider @Inject lateinit var markdownProvider: MarkdownProvider
@Inject lateinit var taskEditEventBus: TaskEditEventBus @Inject lateinit var taskEditEventBus: TaskEditEventBus
@Inject lateinit var localBroadcastManager: LocalBroadcastManager @Inject lateinit var localBroadcastManager: LocalBroadcastManager
@Inject lateinit var locale: Locale
@Inject lateinit var chipProvider: ChipProvider
val editViewModel: TaskEditViewModel by viewModels() val editViewModel: TaskEditViewModel by viewModels()
val subtaskViewModel: TaskListViewModel by viewModels() val subtaskViewModel: TaskListViewModel by viewModels()
@ -184,32 +209,35 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
} }
commentsController.initialize(model, binding.comments) commentsController.initialize(model, binding.comments)
commentsController.reloadView() commentsController.reloadView()
val fragmentManager = childFragmentManager binding.composeView.setContent {
val taskEditControlFragments = MdcTheme {
taskEditControlSetFragmentManager.getOrCreateFragments(fragmentManager) Column {
val visibleSize = taskEditControlSetFragmentManager.visibleSize taskEditControlSetFragmentManager.displayOrder.forEachIndexed { index, tag ->
val fragmentTransaction = fragmentManager.beginTransaction() if (index < taskEditControlSetFragmentManager.visibleSize) {
for (i in taskEditControlFragments.indices) { // TODO: remove ui-viewbinding library when these are all migrated
val taskEditControlFragment = taskEditControlFragments[i] when (taskEditControlSetFragmentManager.controlSetFragments[tag]) {
val tag = getString(taskEditControlFragment.controlId()) TAG_DUE_DATE -> DueDateRow()
fragmentTransaction.replace( TAG_PRIORITY -> PriorityRow()
TaskEditControlSetFragmentManager.TASK_EDIT_CONTROL_FRAGMENT_ROWS[i], TAG_DESCRIPTION -> DescriptionRow()
taskEditControlFragment, TAG_LIST -> ListRow()
tag) TAG_CREATION -> CreationRow()
if (i >= visibleSize) { CalendarControlSet.TAG -> AndroidViewBinding(TaskEditCalendarBinding::inflate)
fragmentTransaction.hide(taskEditControlFragment) StartDateControlSet.TAG -> AndroidViewBinding(TaskEditStartDateBinding::inflate)
} ReminderControlSet.TAG -> AndroidViewBinding(TaskEditRemindersBinding::inflate)
} LocationControlSet.TAG -> AndroidViewBinding(TaskEditLocationBinding::inflate)
fragmentTransaction.replace( FilesControlSet.TAG -> AndroidViewBinding(TaskEditFilesBinding::inflate)
R.id.comment_bar, TimerControlSet.TAG -> AndroidViewBinding(TaskEditTimerBinding::inflate)
fragmentManager.findFragmentByTag(FRAG_TAG_COMMENT_BAR) ?: CommentBarFragment(), TagsControlSet.TAG -> AndroidViewBinding(TaskEditTagsBinding::inflate)
FRAG_TAG_COMMENT_BAR RepeatControlSet.TAG -> AndroidViewBinding(TaskEditRepeatBinding::inflate)
) SubtaskControlSet.TAG -> AndroidViewBinding(TaskEditSubtasksBinding::inflate)
fragmentTransaction.commit() else -> throw IllegalArgumentException("Unknown row: $tag")
for (i in visibleSize - 1 downTo 1) { }
binding.controlSets.addView(inflater.inflate(R.layout.task_edit_row_divider, binding.controlSets, false), i) Divider(modifier = Modifier.fillMaxWidth())
}
}
}
}
} }
return view return view
} }
@ -228,15 +256,6 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
} }
} }
private val beastMode = registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
val transaction = childFragmentManager.beginTransaction()
taskEditControlSetFragmentManager.getOrCreateFragments(childFragmentManager).forEach {
transaction.remove(it)
}
transaction.commit()
activity?.recreate()
}
@OptIn(ExperimentalAnimationApi::class) @OptIn(ExperimentalAnimationApi::class)
private fun showBeastModeHint() { private fun showBeastModeHint() {
binding.banner.setContent { binding.banner.setContent {
@ -248,7 +267,9 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
showSettings = { showSettings = {
visible = false visible = false
preferences.shownBeastModeHint = true preferences.shownBeastModeHint = true
beastMode.launch(Intent(context, BeastModePreferences::class.java)) registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
activity?.recreate()
}.launch(Intent(context, BeastModePreferences::class.java))
firebase.logEvent(R.string.event_banner_beast, R.string.param_click to true) firebase.logEvent(R.string.event_banner_beast, R.string.param_click to true)
}, },
dismiss = { dismiss = {
@ -318,11 +339,6 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
suspend fun save(remove: Boolean = true) = editViewModel.save(remove) suspend fun save(remove: Boolean = true) = editViewModel.save(remove)
/*
* ======================================================================
* ======================================================= event handlers
* ======================================================================
*/
fun discardButtonClick() { fun discardButtonClick() {
if (editViewModel.hasChanges()) { if (editViewModel.hasChanges()) {
dialogBuilder dialogBuilder
@ -351,12 +367,6 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
editViewModel.delete() editViewModel.delete()
} }
/*
* ======================================================================
* ========================================== UI component helper classes
* ======================================================================
*/
fun addComment(message: String?, picture: Uri?) { fun addComment(message: String?, picture: Uri?) {
val model = editViewModel.task val model = editViewModel.task
val userActivity = UserActivity() val userActivity = UserActivity()
@ -375,21 +385,144 @@ class TaskEditFragment : Fragment(), Toolbar.OnMenuItemClickListener {
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when (requestCode) {
REQUEST_DATE -> {
if (resultCode == Activity.RESULT_OK) {
editViewModel.setDueDate(data!!.getLongExtra(DateTimePicker.EXTRA_TIMESTAMP, 0L))
}
}
REQUEST_CODE_SELECT_LIST -> {
if (resultCode == Activity.RESULT_OK) {
data?.getParcelableExtra<Filter>(ListPicker.EXTRA_SELECTED_FILTER)?.let {
if (it is GtasksFilter || it is CaldavFilter) {
editViewModel.selectedList.value = it
} else {
throw RuntimeException("Unhandled filter type")
}
}
}
}
REQUEST_CODE_PICK_CALENDAR -> {
if (resultCode == Activity.RESULT_OK) {
editViewModel.selectedCalendar.value =
data!!.getStringExtra(CalendarPicker.EXTRA_CALENDAR_ID)
}
}
else -> super.onActivityResult(requestCode, resultCode, data)
}
}
private inner class RefreshReceiver : BroadcastReceiver() { private inner class RefreshReceiver : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
subtaskViewModel.invalidate() subtaskViewModel.invalidate()
} }
} }
@Composable
private fun DueDateRow() {
val dueDate = editViewModel.dueDate.collectAsStateLifecycleAware().value
DueDateRow(
dueDate = if (dueDate == 0L) {
null
} else {
DateUtilities.getRelativeDateTime(
LocalContext.current,
dueDate,
locale,
FormatStyle.FULL,
preferences.alwaysDisplayFullDate,
false
)
},
overdue = dueDate.isOverdue,
onClick = {
DateTimePicker
.newDateTimePicker(
this@TaskEditFragment,
REQUEST_DATE,
editViewModel.dueDate.value,
preferences.getBoolean(
R.string.p_auto_dismiss_datetime_edit_screen,
false
)
)
.show(parentFragmentManager, FRAG_TAG_DATE_PICKER)
}
)
}
@Composable
private fun PriorityRow() {
PriorityRow(
priority = editViewModel.priority.collectAsStateLifecycleAware().value,
onChangePriority = { editViewModel.priority.value = it },
desaturate = preferences.desaturateDarkMode,
)
}
@Composable
private fun DescriptionRow() {
DescriptionRow(
text = editViewModel.description.stripCarriageReturns(),
onChanged = { text -> editViewModel.description = text.toString().trim { it <= ' ' } },
linkify = if (preferences.linkify) linkify else null,
markdownProvider = markdownProvider,
)
}
@Composable
private fun ListRow() {
ListRow(
list = editViewModel.selectedList.collectAsStateLifecycleAware().value,
colorProvider = { chipProvider.getColor(it) },
onClick = {
ListPicker
.newListPicker(
editViewModel.selectedList.value,
this@TaskEditFragment,
REQUEST_CODE_SELECT_LIST
)
.show(
parentFragmentManager,
FRAG_TAG_GOOGLE_TASK_LIST_SELECTION
)
}
)
}
@Composable
fun CreationRow() {
InfoRow(
creationDate = editViewModel.creationDate,
modificationDate = editViewModel.modificationDate,
completionDate = editViewModel.completionDate,
locale = locale,
)
}
companion object { companion object {
const val TAG_TASKEDIT_FRAGMENT = "taskedit_fragment" const val TAG_TASKEDIT_FRAGMENT = "taskedit_fragment"
private const val FRAG_TAG_COMMENT_BAR = "comment_bar"
const val EXTRA_TASK = "extra_task" const val EXTRA_TASK = "extra_task"
const val EXTRA_LIST = "extra_list" const val EXTRA_LIST = "extra_list"
const val EXTRA_LOCATION = "extra_location" const val EXTRA_LOCATION = "extra_location"
const val EXTRA_TAGS = "extra_tags" const val EXTRA_TAGS = "extra_tags"
const val EXTRA_ALARMS = "extra_alarms" const val EXTRA_ALARMS = "extra_alarms"
private const val FRAG_TAG_GOOGLE_TASK_LIST_SELECTION = "frag_tag_google_task_list_selection"
const val FRAG_TAG_CALENDAR_PICKER = "frag_tag_calendar_picker"
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker"
private const val REQUEST_CODE_SELECT_LIST = 10101
const val REQUEST_CODE_PICK_CALENDAR = 70
private const val REQUEST_DATE = 504
val Long.isOverdue: Boolean
get() = if (Task.hasDueTime(this)) {
newDateTime(this).isBeforeNow
} else {
newDateTime(this).endOfDay().isBeforeNow
}
fun newTaskEditFragment( fun newTaskEditFragment(
task: Task, task: Task,
list: Filter, list: Filter,

@ -1,7 +1,6 @@
package org.tasks.fragments package org.tasks.fragments
import android.content.Context import android.content.Context
import androidx.fragment.app.FragmentManager
import com.todoroo.astrid.activity.BeastModePreferences import com.todoroo.astrid.activity.BeastModePreferences
import com.todoroo.astrid.files.FilesControlSet import com.todoroo.astrid.files.FilesControlSet
import com.todoroo.astrid.repeats.RepeatControlSet import com.todoroo.astrid.repeats.RepeatControlSet
@ -10,52 +9,21 @@ import com.todoroo.astrid.timers.TimerControlSet
import com.todoroo.astrid.ui.ReminderControlSet import com.todoroo.astrid.ui.ReminderControlSet
import com.todoroo.astrid.ui.StartDateControlSet import com.todoroo.astrid.ui.StartDateControlSet
import dagger.hilt.android.qualifiers.ActivityContext import dagger.hilt.android.qualifiers.ActivityContext
import org.tasks.BuildConfig
import org.tasks.R import org.tasks.R
import org.tasks.preferences.Preferences import org.tasks.preferences.Preferences
import org.tasks.ui.* import org.tasks.ui.CalendarControlSet
import org.tasks.ui.LocationControlSet
import org.tasks.ui.SubtaskControlSet
import javax.inject.Inject import javax.inject.Inject
class TaskEditControlSetFragmentManager @Inject constructor( class TaskEditControlSetFragmentManager @Inject constructor(
@ActivityContext context: Context, @ActivityContext context: Context,
preferences: Preferences?) { preferences: Preferences?
) {
private val controlSetFragments: MutableMap<String, Int> = LinkedHashMap() val controlSetFragments: MutableMap<String, Int> = LinkedHashMap()
private val displayOrder: List<String> val displayOrder: List<String>
var visibleSize = 0 var visibleSize = 0
fun getOrCreateFragments(fragmentManager: FragmentManager): List<TaskEditControlFragment> {
val fragments: MutableList<TaskEditControlFragment> = ArrayList()
for (i in displayOrder.indices) {
val tag = displayOrder[i]
var fragment = fragmentManager.findFragmentByTag(tag) as TaskEditControlFragment?
if (fragment == null) {
val resId = controlSetFragments[tag]
fragment = createFragment(resId!!)
}
fragments.add(fragment)
}
return fragments
}
private fun createFragment(fragmentId: Int): TaskEditControlFragment = when (fragmentId) {
DeadlineControlSet.TAG -> DeadlineControlSet()
PriorityControlSet.TAG -> PriorityControlSet()
DescriptionControlSet.TAG -> DescriptionControlSet()
CalendarControlSet.TAG -> CalendarControlSet()
StartDateControlSet.TAG -> StartDateControlSet()
ReminderControlSet.TAG -> ReminderControlSet()
LocationControlSet.TAG -> LocationControlSet()
FilesControlSet.TAG -> FilesControlSet()
TimerControlSet.TAG -> TimerControlSet()
TagsControlSet.TAG -> TagsControlSet()
RepeatControlSet.TAG -> RepeatControlSet()
ListFragment.TAG -> ListFragment()
SubtaskControlSet.TAG -> SubtaskControlSet()
CreationDateControlSet.TAG -> CreationDateControlSet()
else -> throw RuntimeException("Unsupported fragment")
}
init { init {
displayOrder = BeastModePreferences.constructOrderedControlList(preferences, context) displayOrder = BeastModePreferences.constructOrderedControlList(preferences, context)
val hideAlwaysTrigger = context.getString(R.string.TEA_ctrl_hide_section_pref) val hideAlwaysTrigger = context.getString(R.string.TEA_ctrl_hide_section_pref)
@ -73,44 +41,27 @@ class TaskEditControlSetFragmentManager @Inject constructor(
} }
companion object { companion object {
val TASK_EDIT_CONTROL_FRAGMENT_ROWS = intArrayOf( const val TAG_DESCRIPTION = R.string.TEA_ctrl_notes_pref
R.id.row_1, const val TAG_CREATION = R.string.TEA_ctrl_creation_date
R.id.row_2, const val TAG_LIST = R.string.TEA_ctrl_google_task_list
R.id.row_3, const val TAG_PRIORITY = R.string.TEA_ctrl_importance_pref
R.id.row_4, const val TAG_DUE_DATE = R.string.TEA_ctrl_when_pref
R.id.row_5,
R.id.row_6,
R.id.row_7,
R.id.row_8,
R.id.row_9,
R.id.row_10,
R.id.row_11,
R.id.row_12,
R.id.row_13,
R.id.row_14
)
private val TASK_EDIT_CONTROL_SET_FRAGMENTS = intArrayOf( private val TASK_EDIT_CONTROL_SET_FRAGMENTS = intArrayOf(
DeadlineControlSet.TAG, TAG_DUE_DATE,
TimerControlSet.TAG, TimerControlSet.TAG,
DescriptionControlSet.TAG, TAG_DESCRIPTION,
CalendarControlSet.TAG, CalendarControlSet.TAG,
PriorityControlSet.TAG, TAG_PRIORITY,
StartDateControlSet.TAG, StartDateControlSet.TAG,
ReminderControlSet.TAG, ReminderControlSet.TAG,
LocationControlSet.TAG, LocationControlSet.TAG,
FilesControlSet.TAG, FilesControlSet.TAG,
TagsControlSet.TAG, TagsControlSet.TAG,
RepeatControlSet.TAG, RepeatControlSet.TAG,
CreationDateControlSet.TAG, TAG_CREATION,
ListFragment.TAG, TAG_LIST,
SubtaskControlSet.TAG SubtaskControlSet.TAG
) )
init {
if (BuildConfig.DEBUG
&& TASK_EDIT_CONTROL_FRAGMENT_ROWS.size != TASK_EDIT_CONTROL_SET_FRAGMENTS.size) {
throw AssertionError()
}
}
} }
} }

@ -9,6 +9,7 @@ import android.view.ViewGroup
import android.widget.Toast.LENGTH_SHORT import android.widget.Toast.LENGTH_SHORT
import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ComposeView
import com.google.android.material.composethemeadapter.MdcTheme import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.astrid.activity.TaskEditFragment
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R import org.tasks.R
import org.tasks.Strings.isNullOrEmpty import org.tasks.Strings.isNullOrEmpty
@ -52,8 +53,16 @@ class CalendarControlSet : TaskEditControlFragment() {
}, },
onClick = { onClick = {
if (viewModel.eventUri.value.isNullOrBlank()) { if (viewModel.eventUri.value.isNullOrBlank()) {
CalendarPicker.newCalendarPicker(this@CalendarControlSet, REQUEST_CODE_PICK_CALENDAR, calendarName) CalendarPicker
.show(parentFragmentManager, FRAG_TAG_CALENDAR_PICKER) .newCalendarPicker(
requireParentFragment(),
TaskEditFragment.REQUEST_CODE_PICK_CALENDAR,
calendarName
)
.show(
requireParentFragment().parentFragmentManager,
TaskEditFragment.FRAG_TAG_CALENDAR_PICKER
)
} else { } else {
openCalendarEvent() openCalendarEvent()
} }
@ -95,16 +104,6 @@ class CalendarControlSet : TaskEditControlFragment() {
} }
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_PICK_CALENDAR) {
if (resultCode == Activity.RESULT_OK) {
viewModel.selectedCalendar.value = data!!.getStringExtra(CalendarPicker.EXTRA_CALENDAR_ID)
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
private val calendarName: String? private val calendarName: String?
get() = viewModel.selectedCalendar.value?.let { calendarProvider.getCalendar(it)?.name } get() = viewModel.selectedCalendar.value?.let { calendarProvider.getCalendar(it)?.name }
@ -129,7 +128,5 @@ class CalendarControlSet : TaskEditControlFragment() {
companion object { companion object {
const val TAG = R.string.TEA_ctrl_gcal const val TAG = R.string.TEA_ctrl_gcal
private const val FRAG_TAG_CALENDAR_PICKER = "frag_tag_calendar_picker"
private const val REQUEST_CODE_PICK_CALENDAR = 70
} }
} }

@ -1,36 +0,0 @@
package org.tasks.ui
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.compose.edit.InfoRow
import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
class CreationDateControlSet : TaskEditControlFragment() {
@Inject lateinit var locale: Locale
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
InfoRow(
creationDate = viewModel.creationDate,
modificationDate = viewModel.modificationDate,
completionDate = viewModel.completionDate,
locale = locale,
)
}
}
}
override fun controlId() = TAG
companion object {
const val TAG = R.string.TEA_ctrl_creation_date
}
}

@ -1,86 +0,0 @@
package org.tasks.ui
import android.app.Activity
import android.content.Intent
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import androidx.compose.ui.platform.LocalContext
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.andlib.utility.DateUtilities
import com.todoroo.astrid.data.Task.Companion.hasDueTime
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.DueDateRow
import org.tasks.date.DateTimeUtils
import org.tasks.dialogs.DateTimePicker
import org.tasks.preferences.Preferences
import java.time.format.FormatStyle
import java.util.*
import javax.inject.Inject
@AndroidEntryPoint
class DeadlineControlSet : TaskEditControlFragment() {
@Inject lateinit var locale: Locale
@Inject lateinit var preferences: Preferences
override fun bind(parent: ViewGroup?) =
(parent as ComposeView).apply {
setContent {
MdcTheme {
val dueDate = viewModel.dueDate.collectAsStateLifecycleAware().value
DueDateRow(
dueDate = if (dueDate == 0L) {
null
} else {
DateUtilities.getRelativeDateTime(
LocalContext.current,
dueDate,
locale,
FormatStyle.FULL,
preferences.alwaysDisplayFullDate,
false
)
},
overdue = dueDate.isOverdue,
onClick = {
val fragmentManager = parentFragmentManager
if (fragmentManager.findFragmentByTag(FRAG_TAG_DATE_PICKER) == null) {
DateTimePicker.newDateTimePicker(
this@DeadlineControlSet,
REQUEST_DATE,
viewModel.dueDate.value,
preferences.getBoolean(R.string.p_auto_dismiss_datetime_edit_screen, false))
.show(fragmentManager, FRAG_TAG_DATE_PICKER)
}
}
)
}
}
}
override fun controlId() = TAG
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_DATE) {
if (resultCode == Activity.RESULT_OK) {
viewModel.setDueDate(data!!.getLongExtra(DateTimePicker.EXTRA_TIMESTAMP, 0L))
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
companion object {
const val TAG = R.string.TEA_ctrl_when_pref
private const val REQUEST_DATE = 504
private const val FRAG_TAG_DATE_PICKER = "frag_tag_date_picker"
val Long.isOverdue: Boolean
get() = if (hasDueTime(this)) {
DateTimeUtils.newDateTime(this).isBeforeNow
} else {
DateTimeUtils.newDateTime(this).endOfDay().isBeforeNow
}
}
}

@ -1,41 +0,0 @@
package org.tasks.ui
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.compose.edit.DescriptionRow
import org.tasks.dialogs.Linkify
import org.tasks.markdown.MarkdownProvider
import org.tasks.preferences.Preferences
import org.tasks.ui.TaskEditViewModel.Companion.stripCarriageReturns
import javax.inject.Inject
@AndroidEntryPoint
class DescriptionControlSet : TaskEditControlFragment() {
@Inject lateinit var linkify: Linkify
@Inject lateinit var preferences: Preferences
@Inject lateinit var markdownProvider: MarkdownProvider
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
DescriptionRow(
text = viewModel.description.stripCarriageReturns(),
onChanged = { text -> viewModel.description = text.toString().trim { it <= ' ' } },
linkify = if (preferences.linkify) linkify else null,
markdownProvider = markdownProvider,
)
}
}
}
override fun controlId() = TAG
companion object {
const val TAG = R.string.TEA_ctrl_notes_pref
}
}

@ -1,67 +0,0 @@
package org.tasks.ui
import android.app.Activity
import android.content.Intent
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import com.google.android.material.composethemeadapter.MdcTheme
import com.todoroo.astrid.api.CaldavFilter
import com.todoroo.astrid.api.Filter
import com.todoroo.astrid.api.GtasksFilter
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.activities.ListPicker
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.ListRow
import javax.inject.Inject
@AndroidEntryPoint
class ListFragment : TaskEditControlFragment() {
@Inject lateinit var chipProvider: ChipProvider
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
ListRow(
list = viewModel.selectedList.collectAsStateLifecycleAware().value,
colorProvider = { chipProvider.getColor(it) },
onClick = {
ListPicker.newListPicker(
viewModel.selectedList.value,
this@ListFragment,
REQUEST_CODE_SELECT_LIST
)
.show(parentFragmentManager, FRAG_TAG_GOOGLE_TASK_LIST_SELECTION)
}
)
}
}
}
override fun controlId() = TAG
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (requestCode == REQUEST_CODE_SELECT_LIST) {
if (resultCode == Activity.RESULT_OK) {
data?.getParcelableExtra<Filter>(ListPicker.EXTRA_SELECTED_FILTER)?.let {
if (it is GtasksFilter || it is CaldavFilter) {
viewModel.selectedList.value = it
} else {
throw RuntimeException("Unhandled filter type")
}
}
}
} else {
super.onActivityResult(requestCode, resultCode, data)
}
}
companion object {
const val TAG = R.string.TEA_ctrl_google_task_list
private const val FRAG_TAG_GOOGLE_TASK_LIST_SELECTION = "frag_tag_google_task_list_selection"
private const val REQUEST_CODE_SELECT_LIST = 10101
}
}

@ -1,36 +0,0 @@
package org.tasks.ui
import android.view.View
import android.view.ViewGroup
import androidx.compose.ui.platform.ComposeView
import com.google.android.material.composethemeadapter.MdcTheme
import dagger.hilt.android.AndroidEntryPoint
import org.tasks.R
import org.tasks.compose.collectAsStateLifecycleAware
import org.tasks.compose.edit.PriorityRow
import org.tasks.preferences.Preferences
import javax.inject.Inject
@AndroidEntryPoint
class PriorityControlSet : TaskEditControlFragment() {
@Inject lateinit var preferences: Preferences
override fun bind(parent: ViewGroup?): View =
(parent as ComposeView).apply {
setContent {
MdcTheme {
PriorityRow(
priority = viewModel.priority.collectAsStateLifecycleAware().value,
onChangePriority = { viewModel.priority.value = it },
desaturate = preferences.desaturateDarkMode,
)
}
}
}
override fun controlId() = TAG
companion object {
const val TAG = R.string.TEA_ctrl_importance_pref
}
}

@ -86,73 +86,8 @@
android:gravity="center_horizontal" android:gravity="center_horizontal"
android:orientation="vertical"> android:orientation="vertical">
<FrameLayout <androidx.compose.ui.platform.ComposeView
android:id="@+id/row_1" android:id="@+id/compose_view"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_2"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_3"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_4"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_5"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_6"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_7"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_8"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_9"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_10"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_11"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_12"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_13"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<FrameLayout
android:id="@+id/row_14"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@ -165,11 +100,12 @@
</LinearLayout> </LinearLayout>
</androidx.core.widget.NestedScrollView> </androidx.core.widget.NestedScrollView>
<FrameLayout <androidx.fragment.app.FragmentContainerView
android:id="@+id/comment_bar" android:id="@+id/comment_bar"
android:name="org.tasks.fragments.CommentBarFragment"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:elevation="@dimen/elevation_toolbar" /> android:elevation="8dp"/>
</LinearLayout> </LinearLayout>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_calendar"
android:name="org.tasks.ui.CalendarControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_files"
android:name="com.todoroo.astrid.files.FilesControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_location"
android:name="org.tasks.ui.LocationControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_reminders"
android:name="com.todoroo.astrid.ui.ReminderControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_repeat"
android:name="com.todoroo.astrid.repeats.RepeatControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -1,2 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<View style="@style/task_edit_row_divider"/>

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_start_date"
android:name="com.todoroo.astrid.ui.StartDateControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_subtasks"
android:name="org.tasks.ui.SubtaskControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_tags"
android:name="com.todoroo.astrid.tags.TagsControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.fragment.app.FragmentContainerView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/task_edit_timer"
android:name="com.todoroo.astrid.timers.TimerControlSet"
android:layout_width="match_parent"
android:layout_height="wrap_content" />

@ -572,6 +572,12 @@
+| +--- androidx.compose.runtime:runtime:1.0.1 -> 1.2.0 (*) +| +--- androidx.compose.runtime:runtime:1.0.1 -> 1.2.0 (*)
+| +--- androidx.compose.ui:ui:1.0.1 -> 1.2.0 (*) +| +--- androidx.compose.ui:ui:1.0.1 -> 1.2.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.7.0 (*) +| \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.7.0 (*)
++--- androidx.compose.ui:ui-viewbinding:1.2.0
+| +--- androidx.compose.ui:ui:1.2.0 (*)
+| +--- androidx.compose.ui:ui-util:1.2.0 (*)
+| +--- androidx.databinding:viewbinding:4.1.2 -> 7.2.1 (*)
+| +--- androidx.fragment:fragment-ktx:1.3.2 -> 1.4.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.7.0 (*)
++--- com.google.accompanist:accompanist-flowlayout:0.24.12-rc ++--- com.google.accompanist:accompanist-flowlayout:0.24.12-rc
+| +--- androidx.compose.foundation:foundation:1.2.0-rc02 -> 1.2.0 (*) +| +--- androidx.compose.foundation:foundation:1.2.0-rc02 -> 1.2.0 (*)
+| \--- androidx.compose.ui:ui-util:1.2.0-rc02 -> 1.2.0 (*) +| \--- androidx.compose.ui:ui-util:1.2.0-rc02 -> 1.2.0 (*)

@ -708,6 +708,12 @@
+| +--- androidx.compose.runtime:runtime:1.0.1 -> 1.2.0 (*) +| +--- androidx.compose.runtime:runtime:1.0.1 -> 1.2.0 (*)
+| +--- androidx.compose.ui:ui:1.0.1 -> 1.2.0 (*) +| +--- androidx.compose.ui:ui:1.0.1 -> 1.2.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.7.0 (*) +| \--- org.jetbrains.kotlin:kotlin-stdlib:1.5.30 -> 1.7.0 (*)
++--- androidx.compose.ui:ui-viewbinding:1.2.0
+| +--- androidx.compose.ui:ui:1.2.0 (*)
+| +--- androidx.compose.ui:ui-util:1.2.0 (*)
+| +--- androidx.databinding:viewbinding:4.1.2 -> 7.2.1 (*)
+| +--- androidx.fragment:fragment-ktx:1.3.2 -> 1.4.0 (*)
+| \--- org.jetbrains.kotlin:kotlin-stdlib:1.6.21 -> 1.7.0 (*)
++--- com.google.accompanist:accompanist-flowlayout:0.24.12-rc ++--- com.google.accompanist:accompanist-flowlayout:0.24.12-rc
+| +--- androidx.compose.foundation:foundation:1.2.0-rc02 -> 1.2.0 (*) +| +--- androidx.compose.foundation:foundation:1.2.0-rc02 -> 1.2.0 (*)
+| \--- androidx.compose.ui:ui-util:1.2.0-rc02 -> 1.2.0 (*) +| \--- androidx.compose.ui:ui-util:1.2.0-rc02 -> 1.2.0 (*)

Loading…
Cancel
Save