diff --git a/actions/src/main/kotlin/com/alfresco/content/actions/Action.kt b/actions/src/main/kotlin/com/alfresco/content/actions/Action.kt index 2566d255b..ab6d537f3 100644 --- a/actions/src/main/kotlin/com/alfresco/content/actions/Action.kt +++ b/actions/src/main/kotlin/com/alfresco/content/actions/Action.kt @@ -56,7 +56,7 @@ interface Action { AnalyticsManager().apiTracker(APIEvent.NewFolder, status) } - fun showToast(view: View, anchorView: View? = null) + fun showToast(view: View, anchorView: View? = null) {} fun maxFileNameInToast(view: View) = view.context.resources.getInteger(R.integer.action_toast_file_name_max_length) diff --git a/actions/src/main/kotlin/com/alfresco/content/actions/ActionCreateTask.kt b/actions/src/main/kotlin/com/alfresco/content/actions/ActionCreateTask.kt index c86a8c04b..5ad0eab4a 100644 --- a/actions/src/main/kotlin/com/alfresco/content/actions/ActionCreateTask.kt +++ b/actions/src/main/kotlin/com/alfresco/content/actions/ActionCreateTask.kt @@ -1,7 +1,6 @@ package com.alfresco.content.actions import android.content.Context -import android.view.View import com.alfresco.content.data.AnalyticsManager import com.alfresco.content.data.EventName import com.alfresco.content.data.ParentEntry @@ -40,7 +39,4 @@ data class ActionCreateTask( } override fun copy(_entry: ParentEntry): Action = copy(entry = _entry as TaskEntry) - - override fun showToast(view: View, anchorView: View?) = - Action.showToast(view, anchorView, R.string.action_create_folder_toast, entry.name) } diff --git a/actions/src/main/res/values/strings.xml b/actions/src/main/res/values/strings.xml index c50083c73..2ae714dfc 100644 --- a/actions/src/main/res/values/strings.xml +++ b/actions/src/main/res/values/strings.xml @@ -72,5 +72,7 @@ Rename %s was renamed Save + Edit + Done diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailFragment.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailFragment.kt index 02a235920..153818296 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailFragment.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailFragment.kt @@ -6,6 +6,9 @@ import android.os.Bundle import android.text.TextUtils import android.util.TypedValue import android.view.LayoutInflater +import android.view.Menu +import android.view.MenuInflater +import android.view.MenuItem import android.view.View import android.view.ViewGroup import androidx.appcompat.app.AlertDialog @@ -27,6 +30,7 @@ import com.alfresco.content.browse.databinding.FragmentTaskDetailBinding import com.alfresco.content.browse.databinding.ViewListCommentRowBinding import com.alfresco.content.browse.preview.LocalPreviewActivity import com.alfresco.content.browse.preview.LocalPreviewActivity.Companion.KEY_ENTRY_OBJ +import com.alfresco.content.browse.tasks.TaskViewerActivity import com.alfresco.content.browse.tasks.attachments.listViewAttachmentRow import com.alfresco.content.component.ComponentBuilder import com.alfresco.content.component.ComponentData @@ -66,8 +70,10 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { private lateinit var commentViewBinding: ViewListCommentRowBinding private val epoxyAttachmentController: AsyncEpoxyController by lazy { epoxyAttachmentController() } private var taskCompleteConfirmationDialog = WeakReference(null) + private var saveProgressConfirmationDialog = WeakReference(null) private var viewLayout: View? = null private val dispatcher: CoroutineDispatcher = Dispatchers.Main + private lateinit var menuDetail: Menu override fun onCreateView( inflater: LayoutInflater, @@ -85,11 +91,21 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) AnalyticsManager().screenViewEvent(PageView.TaskView) + (requireActivity() as TaskViewerActivity).setSupportActionBar(binding.toolbar) + withState(viewModel) { state -> + if (!viewModel.isTaskCompleted(state)) { + setHasOptionsMenu(true) + } + } binding.toolbar.apply { navigationContentDescription = getString(R.string.label_navigation_back) navigationIcon = requireContext().getDrawableForAttribute(R.attr.homeAsUpIndicator) - setNavigationOnClickListener { requireActivity().onBackPressed() } + setNavigationOnClickListener { + if (viewModel.hasTaskEditMode) + saveProgressPrompt() + else requireActivity().onBackPressed() + } title = resources.getString(R.string.title_task_view) } binding.recyclerViewAttachments.setController(epoxyAttachmentController) @@ -97,6 +113,56 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { setListeners() } + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.menu_task_detail, menu) + menuDetail = menu + withState(viewModel) { state -> + if (state.parent?.isNewTaskCreated == true) { + menu.findItem(R.id.action_edit).isVisible = false + menu.findItem(R.id.action_done).isVisible = true + updateTaskDetailUI(true) + } else { + menu.findItem(R.id.action_done).isVisible = false + menu.findItem(R.id.action_edit).isVisible = true + } + } + } + + override fun onOptionsItemSelected(item: MenuItem): Boolean { + return when (item.itemId) { + R.id.action_edit -> { + item.isVisible = false + updateTaskDetailUI(true) + menuDetail.findItem(R.id.action_done).isVisible = true + true + } + R.id.action_done -> { + item.isVisible = false + updateTaskDetailUI(false) + menuDetail.findItem(R.id.action_edit).isVisible = true + true + } + else -> super.onOptionsItemSelected(item) + } + } + + private fun updateTaskDetailUI(isEdit: Boolean) { + viewModel.hasTaskEditMode = isEdit + if (isEdit) { + binding.iconTitleEdit.visibility = View.VISIBLE + binding.iconDueDateClear.visibility = View.VISIBLE + binding.iconPriorityEdit.visibility = View.VISIBLE + binding.iconAssignedEdit.visibility = View.VISIBLE + binding.completeButton.isEnabled = false + } else { + binding.iconTitleEdit.visibility = View.GONE + binding.iconDueDateClear.visibility = View.INVISIBLE + binding.iconPriorityEdit.visibility = View.INVISIBLE + binding.iconAssignedEdit.visibility = View.INVISIBLE + binding.completeButton.isEnabled = true + } + } + private fun setListeners() { viewModel.setListener(this) binding.tvAddComment.setOnClickListener { @@ -182,7 +248,7 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { binding.tvIdentifierValue.text = dataObj.id binding.tvTaskDescription.text = if (dataObj.description.isNullOrEmpty()) requireContext().getString(R.string.empty_description) else dataObj.description - binding.tvTaskDescription.addTextViewPrefix(requireContext().getString(R.string.text_view_all)) { + binding.tvTaskDescription.addTextViewPrefix(requireContext().getString(R.string.suffix_view_all)) { viewLifecycleOwner.lifecycleScope.launch { showComponentSheetDialog( requireContext(), ComponentData( @@ -198,27 +264,20 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { if (viewModel.isTaskCompleted(state)) { binding.tvAddComment.visibility = View.GONE binding.iconAddCommentUser.visibility = View.GONE - binding.iconCompleted.visibility = View.VISIBLE - binding.tvCompletedTitle.visibility = View.VISIBLE - binding.tvCompletedValue.visibility = View.VISIBLE + binding.clCompleted.visibility = View.VISIBLE if (state.listComments.isEmpty()) binding.viewComment2.visibility = View.GONE else View.VISIBLE binding.tvCompletedValue.text = dataObj.endDate?.toLocalDate().toString().getDateZoneFormat(DATE_FORMAT_1, DATE_FORMAT_4) - (binding.iconDueDate.layoutParams as ConstraintLayout.LayoutParams).apply { + + (binding.clDueDate.layoutParams as ConstraintLayout.LayoutParams).apply { topMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 24f, resources.displayMetrics).toInt() } - binding.iconStatus.visibility = View.GONE - binding.tvStatusTitle.visibility = View.GONE - binding.tvStatusValue.visibility = View.GONE + binding.clStatus.visibility = View.GONE } else { - binding.iconCompleted.visibility = View.GONE - binding.tvCompletedTitle.visibility = View.GONE - binding.tvCompletedValue.visibility = View.GONE - (binding.iconDueDate.layoutParams as ConstraintLayout.LayoutParams).apply { + binding.clCompleted.visibility = View.GONE + (binding.clDueDate.layoutParams as ConstraintLayout.LayoutParams).apply { topMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 0f, resources.displayMetrics).toInt() } - binding.iconStatus.visibility = View.VISIBLE - binding.tvStatusTitle.visibility = View.VISIBLE - binding.tvStatusValue.visibility = View.VISIBLE + binding.clStatus.visibility = View.VISIBLE binding.tvStatusValue.text = getString(R.string.status_active) } } @@ -273,8 +332,8 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { val dialog = MaterialAlertDialogBuilder(requireContext()) .setTitle(getString(R.string.dialog_title_complete_task)) .setMessage(getString(R.string.dialog_message_complete_task)) - .setNegativeButton(getString(R.string.dialog_negative_button_complete_task), null) - .setPositiveButton(getString(R.string.dialog_positive_button_complete_task)) { _, _ -> + .setNegativeButton(getString(R.string.dialog_negative_button_task), null) + .setPositiveButton(getString(R.string.dialog_positive_button_task)) { _, _ -> AnalyticsManager().taskEvent(EventName.TaskComplete) viewModel.completeTask() } @@ -282,6 +341,20 @@ class TaskDetailFragment : Fragment(), MavericksView, EntryListener { taskCompleteConfirmationDialog = WeakReference(dialog) } + private fun saveProgressPrompt() { + val oldDialog = saveProgressConfirmationDialog.get() + if (oldDialog != null && oldDialog.isShowing) return + val dialog = MaterialAlertDialogBuilder(requireContext()) + .setTitle(getString(R.string.dialog_title_save_progress)) + .setMessage(getString(R.string.dialog_message_save_progress)) + .setNegativeButton(getString(R.string.dialog_negative_button_task), null) + .setPositiveButton(getString(R.string.dialog_positive_button_task)) { _, _ -> + requireActivity().onBackPressed() + } + .show() + saveProgressConfirmationDialog = WeakReference(dialog) + } + override fun onEntryCreated(entry: ParentEntry) { if (isAdded) startActivity( diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModel.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModel.kt index 322ae62a1..cad0c08c1 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModel.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewModel.kt @@ -25,6 +25,7 @@ class TaskDetailViewModel( ) : MavericksViewModel(state) { var isAddComment = false + var hasTaskEditMode = false var entryListener: EntryListener? = null init { diff --git a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewState.kt b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewState.kt index bbf0faff0..f8ccd7bf7 100644 --- a/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewState.kt +++ b/browse/src/main/kotlin/com/alfresco/content/browse/tasks/detail/TaskDetailViewState.kt @@ -25,6 +25,7 @@ data class TaskDetailViewState( ) : MavericksState { constructor(target: TaskEntry) : this(parent = target) + /** * update the taskDetailObj params after getting the response from server. */ diff --git a/browse/src/main/res/drawable/ic_clear.xml b/browse/src/main/res/drawable/ic_clear.xml new file mode 100644 index 000000000..df9ee4bbd --- /dev/null +++ b/browse/src/main/res/drawable/ic_clear.xml @@ -0,0 +1,9 @@ + + + diff --git a/browse/src/main/res/drawable/ic_edit.xml b/browse/src/main/res/drawable/ic_edit.xml new file mode 100644 index 000000000..c8d2f36b4 --- /dev/null +++ b/browse/src/main/res/drawable/ic_edit.xml @@ -0,0 +1,10 @@ + + + diff --git a/browse/src/main/res/layout/fragment_task_detail.xml b/browse/src/main/res/layout/fragment_task_detail.xml index 4216350b6..c7cf1889a 100644 --- a/browse/src/main/res/layout/fragment_task_detail.xml +++ b/browse/src/main/res/layout/fragment_task_detail.xml @@ -1,7 +1,6 @@ @@ -10,7 +9,8 @@ android:id="@+id/toolbar" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/bottom_separator" /> + android:background="@drawable/bottom_separator" + android:theme="@style/Theme.Alfresco.toolbar" /> + app:layout_constraintTop_toTopOf="parent" /> + + - - - - + app:layout_constraintTop_toTopOf="parent"> - + - + - + + + - + - - - + app:layout_constraintTop_toBottomOf="@id/cl_completed"> - + - + - + + + - + - - - + app:layout_constraintTop_toBottomOf="@+id/cl_due_date"> - + - + + + + + + + + + + + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/cl_priority"> - + + + + + + + + + + + app:layout_constraintTop_toBottomOf="@+id/cl_assigned"> - + - + + + + + + + + + + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toBottomOf="@+id/cl_status"> + + + + + + + + + @@ -338,7 +496,7 @@ android:orientation="horizontal" app:layout_constraintEnd_toEndOf="parent" app:layout_constraintStart_toStartOf="parent" - app:layout_constraintTop_toBottomOf="@id/cl_due_date"> + app:layout_constraintTop_toBottomOf="@id/cl_details"> diff --git a/browse/src/main/res/menu/menu_task_detail.xml b/browse/src/main/res/menu/menu_task_detail.xml new file mode 100644 index 000000000..e2fd1f9ea --- /dev/null +++ b/browse/src/main/res/menu/menu_task_detail.xml @@ -0,0 +1,14 @@ + + + + + diff --git a/browse/src/main/res/values-de/strings.xml b/browse/src/main/res/values-de/strings.xml index 291563188..93b37cb9f 100755 --- a/browse/src/main/res/values-de/strings.xml +++ b/browse/src/main/res/values-de/strings.xml @@ -71,8 +71,8 @@ Abgeschlossen Aufgabe abschließen Sind Sie sicher, dass Sie die Aufgabe abschließen wollen? Sie werden keine Änderungen mehr vornehmen können. - Abbrechen - Bestätigen + Abbrechen + Bestätigen Schaltfläche „Senden“ diff --git a/browse/src/main/res/values-es/strings.xml b/browse/src/main/res/values-es/strings.xml index 1801f397a..f75cb646a 100755 --- a/browse/src/main/res/values-es/strings.xml +++ b/browse/src/main/res/values-es/strings.xml @@ -71,8 +71,8 @@ Por completar Completar tarea ¿Está seguro de que desea completar la tarea? Ya no podrá realizar ningún cambio. - Cancelar - Confirmar + Cancelar + Confirmar Botón Enviar diff --git a/browse/src/main/res/values-fr/strings.xml b/browse/src/main/res/values-fr/strings.xml index 828112ab6..4aee8bab2 100755 --- a/browse/src/main/res/values-fr/strings.xml +++ b/browse/src/main/res/values-fr/strings.xml @@ -71,8 +71,8 @@ A terminer Terminer la tâche Etes-vous sûr de vouloir terminer la tâche ? Vous ne pourrez plus faire de modifications. - Annuler - Confirmer + Annuler + Confirmer Bouton Envoyer diff --git a/browse/src/main/res/values-it/strings.xml b/browse/src/main/res/values-it/strings.xml index 8bad3890e..2d330376a 100755 --- a/browse/src/main/res/values-it/strings.xml +++ b/browse/src/main/res/values-it/strings.xml @@ -71,8 +71,8 @@ Per completare Completa compito Vuoi completare il compito? Non potrai più apportare modifiche. - Annulla - Conferma + Annulla + Conferma Pulsante Invia diff --git a/browse/src/main/res/values-nl/strings.xml b/browse/src/main/res/values-nl/strings.xml index 77492bbf8..97a88dce3 100755 --- a/browse/src/main/res/values-nl/strings.xml +++ b/browse/src/main/res/values-nl/strings.xml @@ -71,8 +71,8 @@ Voltooien Taak voltooien Weet u zeker dat u de taak wilt voltooien? U kunt geen wijzigingen meer aanbrengen. - Annuleren - Bevestigen + Annuleren + Bevestigen Knop Verzenden diff --git a/browse/src/main/res/values/strings.xml b/browse/src/main/res/values/strings.xml index 3dffbbbc5..5dbe6673c 100644 --- a/browse/src/main/res/values/strings.xml +++ b/browse/src/main/res/values/strings.xml @@ -71,11 +71,17 @@ Complete Complete Task Are you sure you want to complete this task? You will no longer be able to make any changes. - Cancel - Confirm + Cancel + Confirm Send Button No description Completed icon Task + Edit priority icon + Clear due date icon + …View all + Task Progress + Do you want to save progress? + Mark as complete diff --git a/component/src/main/java/com/alfresco/content/component/ComponentSheetExtension.kt b/component/src/main/java/com/alfresco/content/component/ComponentSheetExtension.kt index 3ba5d32a0..ab53f1292 100644 --- a/component/src/main/java/com/alfresco/content/component/ComponentSheetExtension.kt +++ b/component/src/main/java/com/alfresco/content/component/ComponentSheetExtension.kt @@ -181,17 +181,17 @@ fun ComponentSheet.setupFacetComponent(state: ComponentState, viewModel: Compone } /** - * setup the Text Component + * setup the Title Description Component * @param state */ fun ComponentSheet.setupTextComponent(state: ComponentState) { - binding.parentView.addView(binding.frameText) - binding.textComponent.tvTaskTitle.text = state.parent?.query ?: "" - binding.textComponent.tvTaskDescription.text = state.parent?.value ?: "" + binding.parentView.addView(binding.frameTitleDescription) + binding.titleDescriptionComponent.tvTaskTitle.text = state.parent?.query ?: "" + binding.titleDescriptionComponent.tvTaskDescription.text = state.parent?.value ?: "" binding.bottomView.visibility = View.GONE binding.bottomSeparator.visibility = View.GONE - binding.textComponent.componentParent.visibility = View.VISIBLE + binding.titleDescriptionComponent.componentParent.visibility = View.VISIBLE } private fun getRecyclerviewLayoutParams(context: Context, minVisibleItem: Int): LinearLayout.LayoutParams { diff --git a/component/src/main/res/layout/sheet_component_filter.xml b/component/src/main/res/layout/sheet_component_filter.xml index ed7d163f7..7980e5cf6 100644 --- a/component/src/main/res/layout/sheet_component_filter.xml +++ b/component/src/main/res/layout/sheet_component_filter.xml @@ -65,14 +65,14 @@ + android:id="@+id/titleDescriptionComponent" + layout="@layout/view_title_description_component" /> - - - - - - - - - - diff --git a/component/src/main/res/layout/view_title_description_component.xml b/component/src/main/res/layout/view_title_description_component.xml new file mode 100644 index 000000000..49bbb29e5 --- /dev/null +++ b/component/src/main/res/layout/view_title_description_component.xml @@ -0,0 +1,38 @@ + + + + + + + + + + + + + + diff --git a/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt b/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt index 6f7e6c7ff..c83e76bbc 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/TaskEntry.kt @@ -18,7 +18,8 @@ data class TaskEntry( val created: ZonedDateTime? = null, val endDate: ZonedDateTime? = null, val dueDate: ZonedDateTime? = null, - val duration: Long? = null + val duration: Long? = null, + val isNewTaskCreated: Boolean = false ) : ParentEntry(), Parcelable { companion object { @@ -26,7 +27,7 @@ data class TaskEntry( /** * return the TaskEntry obj using TaskDataEntry */ - fun with(data: TaskDataEntry): TaskEntry { + fun with(data: TaskDataEntry, isNewTaskCreated: Boolean = false): TaskEntry { return TaskEntry( id = data.id ?: "", name = data.name ?: "", @@ -36,7 +37,8 @@ data class TaskEntry( priority = data.priority?.toInt() ?: 0, endDate = data.endDate, dueDate = data.dueDate, - duration = data.duration + duration = data.duration, + isNewTaskCreated = isNewTaskCreated ) } } diff --git a/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt b/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt index 51f166e98..7760a2219 100644 --- a/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt +++ b/data/src/main/kotlin/com/alfresco/content/data/TaskRepository.kt @@ -116,7 +116,7 @@ class TaskRepository(val session: Session = SessionManager.requireSession) { name = name, description = description ) - ) + ), true ) } } diff --git a/listview/src/main/kotlin/com/alfresco/content/listview/TextViewExt.kt b/listview/src/main/kotlin/com/alfresco/content/listview/TextViewExt.kt index 93543e31e..fe8eacd06 100644 --- a/listview/src/main/kotlin/com/alfresco/content/listview/TextViewExt.kt +++ b/listview/src/main/kotlin/com/alfresco/content/listview/TextViewExt.kt @@ -61,7 +61,7 @@ fun TextView.addTextViewPrefix(prefix: String, callback: TextViewCallback) { ) spannable.setSpan( ForegroundColorSpan(ContextCompat.getColor(context, R.color.alfresco_blue_700)), - truncatedText.length - prefix.length, + truncatedText.length - prefix.length.minus(1), truncatedText.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) diff --git a/listview/src/main/res/values-night/colors.xml b/listview/src/main/res/values-night/colors.xml index 5f8c2f7df..1b0900ba7 100644 --- a/listview/src/main/res/values-night/colors.xml +++ b/listview/src/main/res/values-night/colors.xml @@ -13,4 +13,5 @@ #0A0A0A #1FFFFFFF #FFFFFF + @color/white_60 diff --git a/listview/src/main/res/values/colors.xml b/listview/src/main/res/values/colors.xml index 81572be3e..fc9630a0c 100644 --- a/listview/src/main/res/values/colors.xml +++ b/listview/src/main/res/values/colors.xml @@ -13,4 +13,5 @@ @color/white #1F212121 #262626 + @color/alfresco_gray_900_30 diff --git a/theme/src/main/res/values/themes.xml b/theme/src/main/res/values/themes.xml index 0cfa51c58..1378e1e17 100644 --- a/theme/src/main/res/values/themes.xml +++ b/theme/src/main/res/values/themes.xml @@ -73,6 +73,12 @@ #2D2D2D + +