From 2ea10966227338be4141f98259fdd400a151823d Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Thu, 17 Nov 2022 17:33:15 +0100 Subject: [PATCH] build basic task summary view --- Habitica/AndroidManifest.xml | 10 ++ Habitica/res/drawable-mdpi/completed.png | Bin 0 -> 396 bytes Habitica/res/drawable-xhdpi/completed.png | Bin 0 -> 509 bytes Habitica/res/drawable-xxhdpi/completed.png | Bin 0 -> 661 bytes Habitica/res/values/strings.xml | 8 + .../helpers/MainNavigationController.kt | 2 +- .../helpers/TaskDescriptionBuilder.kt | 52 ++++++ .../android/habitica/models/tasks/Task.kt | 4 - .../habitica/models/tasks/TaskGroupPlan.kt | 3 +- .../ui/activities/TaskSummaryActivity.kt | 156 +++++++++++++++++- .../tasks/TaskRecyclerViewFragment.kt | 6 +- 11 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 Habitica/res/drawable-mdpi/completed.png create mode 100644 Habitica/res/drawable-xhdpi/completed.png create mode 100644 Habitica/res/drawable-xxhdpi/completed.png create mode 100644 Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskDescriptionBuilder.kt diff --git a/Habitica/AndroidManifest.xml b/Habitica/AndroidManifest.xml index ed0a0b018..798548e15 100644 --- a/Habitica/AndroidManifest.xml +++ b/Habitica/AndroidManifest.xml @@ -148,6 +148,16 @@ android:name="android.support.PARENT_ACTIVITY" android:value=".ui.activities.MainActivity" /> + + + 4R2FDE#L(|8ySX>JrB~@jmLH-lhT3A#GmLPe` qj~^@ig9cjPq|t1B0n>!_U$H00003e=m{Vpu?c~|UKifL9-#EunT0|- znVD8vH~hO!-^~Bb>zn2cL7~|!JZ})50d@gu;^?B6nA!Kd-Y2Qec~gz?fufkvak8Ul z;Kv%wg_z zPFyX37D8dJT2N`8iV}?8Fa0e{07?jjxkxEH*tAzq155MV_(6EkqP`!r+z?D18-k7b;VMO4A2%eiJ4z>0O@5R zJzgB4a&fc+-R1zqbtz`*i0cz7k=)!|cnGg=DCETfhRrLF00000NkvXXu0mjf0(s6j literal 0 HcmV?d00001 diff --git a/Habitica/res/drawable-xxhdpi/completed.png b/Habitica/res/drawable-xxhdpi/completed.png new file mode 100644 index 0000000000000000000000000000000000000000..d84b65cb2426f8fd97b5ada9b0a88c1b82bc445b GIT binary patch literal 661 zcmV;G0&4wB0Ma{|gq(t=PoAlQRMj=&NS^x+q^6@)sm zQ=0^-zfBUScE2x8>K$M$1v8wu}ZC^at&y z7r~K#y)K~v^mycXE|6`}?o#pf{g zwTV5KHu5o0(}Z8~Qyfjq2C<`wFybc=V-1#knuRP)gcT2*-EHn9u;j}$T4AD8ymY+? zgaO~;g}Lj1!9=Bad4EOVJQE?r3&w@4V8TlL^txywl=w;AawM^rqpkQ)jT8EE^da7q z4^$>JRfCn7*VynJ%7o{27kKhh7LJAPHg^*OpTQ9y;sr0koT~|6;stNfsI3W~;svka zbd?F;;sx*dfXPG%@j{SzyK{ruQj(u17pSzEoaPhVm5%MW7OL#`Y!U_z><_Qeq3O~D>Sp+K&!Ao v_D_jefAnkoyV*deuftcY9LMp-qZGUYNj^A)5^n-B00000NkvXXu0mjf_xB{~ literal 0 HcmV?d00001 diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index acd6d9e5e..a6684100c 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -53,6 +53,7 @@ Your Username and/or Password was incorrect. Save Copy + Title Notes Difficulty Tags @@ -1256,6 +1257,13 @@ Copy shared tasks Group Plan Settings Task Summary + This is a **%s** Habit that is **%s**. + This is a **%s** Task that is due **%s**. + This is a **%s** Task that does not have a due date. + This is a **%s** Task that repeats **%s**. + positive and negative + Assigned to + Completed at %s You You, %d other diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/MainNavigationController.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/MainNavigationController.kt index a5c6082ba..a5d4089c1 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/MainNavigationController.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/MainNavigationController.kt @@ -70,6 +70,6 @@ object MainNavigationController { } fun navigateBack() { - navController?.navigateUp() + navController?.popBackStack() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskDescriptionBuilder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskDescriptionBuilder.kt new file mode 100644 index 000000000..bc4c2c40b --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskDescriptionBuilder.kt @@ -0,0 +1,52 @@ +package com.habitrpg.android.habitica.helpers + +import android.content.Context +import com.habitrpg.android.habitica.R +import com.habitrpg.android.habitica.models.tasks.Task +import com.habitrpg.shared.habitica.models.tasks.TaskType +import java.text.DateFormat +import java.util.Date + +class TaskDescriptionBuilder(private val context: Context) { + + fun describe(task: Task): String { + return when (task.type) { + TaskType.HABIT -> context.getString(R.string.habit_summary_description, describeHabitDirections(task.up ?: false, task.down ?: false), describeDifficulty(task.priority)) + TaskType.TODO -> { + if (task.dueDate != null) { + context.getString(R.string.todo_summary_description_duedate, describeDifficulty(task.priority), describeDate(task.dueDate!!)) + } else { + context.getString(R.string.todo_summary_description, describeDifficulty(task.priority)) + } + } + TaskType.DAILY -> context.getString(R.string.daily_summary_description, describeDifficulty(task.priority), "sometimes") + else -> "" + } + } + + private val dateFormatter = DateFormat.getDateInstance() + + private fun describeDate(date: Date): String { + return dateFormatter.format(date) + } + + private fun describeHabitDirections(up: Boolean, down: Boolean): String { + return if (up && down) { + context.getString(R.string.positive_and_negative) + } else if (up) { + context.getString(R.string.positive_habit_form) + } else { + context.getString(R.string.negative_habit_form) + } + } + + private fun describeDifficulty(difficulty: Float): String { + return when (difficulty) { + 0.1f -> context.getString(R.string.trivial) + 1.0f -> context.getString(R.string.easy) + 1.5f -> context.getString(R.string.medium) + 2.0f -> context.getString(R.string.hard) + else -> "" + } + } +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt index 2cd447a1e..19fed52b9 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/Task.kt @@ -501,10 +501,6 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask { return daysOfMonth } - fun canEdit(userID: String): Boolean { - return true - } - companion object CREATOR : Parcelable.Creator { override fun createFromParcel(source: Parcel): Task = Task(source) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/TaskGroupPlan.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/TaskGroupPlan.kt index 2e936526c..e0b16b555 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/TaskGroupPlan.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/tasks/TaskGroupPlan.kt @@ -9,11 +9,12 @@ import java.util.Date @RealmClass(embedded = true) open class GroupAssignedDetails: RealmObject(), BaseObject { - var assignedDate: String? = null + var assignedDate: Date? = null var assignedUsername: String? = null var assignedUserID: String? = null var assigningUsername: String? = null var completed: Boolean = false + var completedDate: Date? = null } @RealmClass(embedded = true) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt index 0e53ac81f..363baaade 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/TaskSummaryActivity.kt @@ -1,21 +1,49 @@ package com.habitrpg.android.habitica.ui.activities import android.os.Bundle +import android.view.Window +import android.view.WindowManager import androidx.activity.compose.setContent +import androidx.activity.viewModels +import androidx.compose.foundation.Image import androidx.compose.foundation.background import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.heightIn +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Button +import androidx.compose.material.ButtonDefaults +import androidx.compose.material.MaterialTheme import androidx.compose.material.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.ColorFilter +import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.tooling.preview.Preview +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.core.content.ContextCompat +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.asLiveData import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.TaskRepository +import com.habitrpg.android.habitica.helpers.MainNavigationController +import com.habitrpg.android.habitica.helpers.TaskDescriptionBuilder import com.habitrpg.android.habitica.ui.viewmodels.BaseViewModel +import com.habitrpg.common.habitica.helpers.MarkdownParser +import com.habitrpg.shared.habitica.models.tasks.TaskType +import java.text.DateFormat import javax.inject.Inject class TaskSummaryViewModel(val taskId: String) : BaseViewModel() { @@ -26,18 +54,36 @@ class TaskSummaryViewModel(val taskId: String) : BaseViewModel() { override fun inject(component: UserComponent) { component.inject(this) } + + @Suppress("UNCHECKED_CAST") + class Factory(private val taskID: String): ViewModelProvider.Factory { + override fun create(modelClass: Class): T { + return TaskSummaryViewModel(taskID) as T + } + } } class TaskSummaryActivity: BaseActivity() { override fun getLayoutResId(): Int? = null + private val viewModel: TaskSummaryViewModel by viewModels { TaskSummaryViewModel.Factory(intent.extras?.getString( + TaskFormActivity.TASK_ID_KEY + ) ?: "") } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { - + TaskSummaryView(viewModel = viewModel) } } + override fun onStart() { + super.onStart() + val window: Window = window + window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) + window.statusBarColor = ContextCompat.getColor(this, viewModel.task.value?.lightTaskColor ?: R.color.brand_300) + } + override fun injectActivity(component: UserComponent?) { component?.inject(this) } @@ -45,9 +91,113 @@ class TaskSummaryActivity: BaseActivity() { @Composable fun TaskSummaryView(viewModel: TaskSummaryViewModel) { + val taskDescriptionBuilder = TaskDescriptionBuilder(LocalContext.current) val task by viewModel.task.observeAsState() - Column { - Text(stringResource(R.string.task_summary), Modifier) + val titleModifier = Modifier.padding(top = 30.dp) + val textModifier = Modifier.padding(top = 4.dp) + val completedTimeFormat = DateFormat.getTimeInstance() + val darkestColor = colorResource(task?.darkestTaskColor ?: R.color.text_primary) + Column(Modifier.background(colorResource(task?.lightTaskColor ?: R.color.brand_300))) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(0.dp, 4.dp)) { + Button(onClick = { + MainNavigationController.navigateBack() + }, colors = ButtonDefaults.textButtonColors(contentColor = darkestColor), elevation = ButtonDefaults.elevation(0.dp)) { + Image(painterResource(R.drawable.ic_arrow_back_white_36dp), stringResource(R.string.action_back), colorFilter = ColorFilter.tint(colorResource(task?.darkestTaskColor ?: R.color.white))) + } + Text( + stringResource(R.string.task_summary), + fontSize = 18.sp, + fontWeight = FontWeight.Medium, + color = darkestColor, + modifier = Modifier.padding(start = 6.dp) + ) + } + Column( + Modifier + .background( + MaterialTheme.colors.background, + RoundedCornerShape(topStart = 12.dp, topEnd = 12.dp) + ) + .padding(20.dp, 5.dp) + .fillMaxWidth()) { + Text(stringResource(R.string.title), fontSize = 16.sp, color = darkestColor, fontWeight = FontWeight.Medium, modifier = titleModifier) + Text(task?.text ?: "", fontSize = 16.sp, color = MaterialTheme.colors.onBackground, modifier = textModifier) + if (task?.notes?.isNotBlank() == true) { + Text( + stringResource(R.string.notes), + fontSize = 16.sp, + color = MaterialTheme.colors.onSecondary, + fontWeight = FontWeight.Medium, + modifier = titleModifier + ) + Text( + task?.notes ?: "", + fontSize = 16.sp, + color = MaterialTheme.colors.onBackground, + modifier = textModifier + ) + } + if (task?.type != TaskType.REWARD) { + Text( + stringResource(R.string.description), + fontSize = 16.sp, + color = darkestColor, + fontWeight = FontWeight.Medium, + modifier = titleModifier + ) + Text(MarkdownParser.parseMarkdown(task?.let { taskDescriptionBuilder.describe(it) } ?: "").toString(), + fontSize = 16.sp, + color = MaterialTheme.colors.onBackground, + modifier = textModifier) + } + if (task?.checklist?.isNotEmpty() == true) { + task?.checklist?.let { checklist -> + Text(stringResource(R.string.checklist), fontSize = 16.sp, color = darkestColor, fontWeight = FontWeight.Medium, modifier = titleModifier) + for (item in checklist) { + Text(item.text ?: "", fontSize = 16.sp, fontWeight = FontWeight.Medium, modifier = Modifier + .background(colorResource(R.color.gray_700)) + .padding(15.dp) + .fillMaxWidth()) + } + } + } + if (task?.group?.assignedUsersDetail?.isNotEmpty() == true) { + Text(stringResource(R.string.assigned_to), fontSize = 16.sp, color = darkestColor, fontWeight = FontWeight.Medium, modifier = titleModifier.padding(bottom = 4.dp)) + for (item in task?.group?.assignedUsersDetail ?: emptyList()) { + UserRow(item.assignedUsername ?: "", Modifier + .padding(vertical = 4.dp) + .background(colorResource(R.color.gray_700), RoundedCornerShape(8.dp)) + .padding(15.dp, 12.dp) + .heightIn(min = 24.dp) + .fillMaxWidth(), + extraContent = if (item.completed) ({ + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.padding(top = 4.dp)) { + Image(painterResource(R.drawable.completed), null) + Text(stringResource(R.string.completed_at, + item.completedDate?.let { completedTimeFormat.format(it) } ?: ""), + fontSize = 14.sp, + color = colorResource(R.color.green_10), modifier = Modifier.padding(start = 4.dp)) + } + }) else null) + } + } + } + } +} + +@Composable +fun UserRow(username: String, modifier: Modifier = Modifier, extraContent: @Composable (() -> Unit)? = null) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) { + Column { + Text( + "@$username", fontSize = 16.sp, fontWeight = FontWeight.Medium, modifier = Modifier + .background(colorResource(R.color.gray_700)) + .fillMaxWidth() + ) + if (extraContent != null) { + extraContent() + } + } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt index 2ca698d74..ab4602dcf 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TaskRecyclerViewFragment.kt @@ -66,7 +66,6 @@ open class TaskRecyclerViewFragment : BaseFragment