mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-18 11:49:01 +00:00
build basic task summary view
This commit is contained in:
parent
3bd68fedf0
commit
2ea1096622
11 changed files with 228 additions and 13 deletions
|
|
@ -148,6 +148,16 @@
|
|||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.activities.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.TaskSummaryActivity"
|
||||
android:parentActivityName=".ui.activities.MainActivity"
|
||||
tools:ignore="UnusedAttribute"
|
||||
android:configChanges="screenSize | smallestScreenSize | screenLayout | orientation"
|
||||
android:windowSoftInputMode="stateVisible|adjustResize">
|
||||
<meta-data
|
||||
android:name="android.support.PARENT_ACTIVITY"
|
||||
android:value=".ui.activities.MainActivity" />
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.activities.GroupFormActivity"
|
||||
android:parentActivityName=".ui.activities.MainActivity"
|
||||
|
|
|
|||
BIN
Habitica/res/drawable-mdpi/completed.png
Normal file
BIN
Habitica/res/drawable-mdpi/completed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 396 B |
BIN
Habitica/res/drawable-xhdpi/completed.png
Normal file
BIN
Habitica/res/drawable-xhdpi/completed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 509 B |
BIN
Habitica/res/drawable-xxhdpi/completed.png
Normal file
BIN
Habitica/res/drawable-xxhdpi/completed.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 661 B |
|
|
@ -53,6 +53,7 @@
|
|||
<string name="authentication_error_body">Your Username and/or Password was incorrect.</string>
|
||||
<string name="save_changes">Save</string>
|
||||
<string name="copy">Copy</string>
|
||||
<string name="title">Title</string>
|
||||
<string name="notes">Notes</string>
|
||||
<string name="difficulty">Difficulty</string>
|
||||
<string name="tags">Tags</string>
|
||||
|
|
@ -1256,6 +1257,13 @@
|
|||
<string name="copy_shared_tasks">Copy shared tasks</string>
|
||||
<string name="group_plan_settings">Group Plan Settings</string>
|
||||
<string name="task_summary">Task Summary</string>
|
||||
<string name="habit_summary_description">This is a **%s** Habit that is **%s**.</string>
|
||||
<string name="todo_summary_description_duedate">This is a **%s** Task that is due **%s**.</string>
|
||||
<string name="todo_summary_description">This is a **%s** Task that does not have a due date.</string>
|
||||
<string name="daily_summary_description">This is a **%s** Task that repeats **%s**.</string>
|
||||
<string name="positive_and_negative">positive and negative</string>
|
||||
<string name="assigned_to">Assigned to</string>
|
||||
<string name="completed_at">Completed at %s</string>
|
||||
<plurals name="you_x_others">
|
||||
<item quantity="zero">You</item>
|
||||
<item quantity="one">You, %d other</item>
|
||||
|
|
|
|||
|
|
@ -70,6 +70,6 @@ object MainNavigationController {
|
|||
}
|
||||
|
||||
fun navigateBack() {
|
||||
navController?.navigateUp()
|
||||
navController?.popBackStack()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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 -> ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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<Task> {
|
||||
override fun createFromParcel(source: Parcel): Task = Task(source)
|
||||
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 <T : ViewModel> create(modelClass: Class<T>): 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -66,7 +66,6 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
private var taskFlowJob: Job? = null
|
||||
val viewModel: TasksViewModel by viewModels({ requireParentFragment() })
|
||||
|
||||
internal var canEditTasks: Boolean = true
|
||||
internal var canScoreTaks: Boolean = true
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
|
|
@ -159,7 +158,6 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
|
||||
viewModel.ownerID.observe(viewLifecycleOwner) {
|
||||
canEditTasks = viewModel.isPersonalBoard
|
||||
canScoreTaks = viewModel.isPersonalBoard
|
||||
updateTaskSubscription(it)
|
||||
}
|
||||
|
|
@ -555,7 +553,7 @@ private fun setPreferenceTaskFilters() {
|
|||
|
||||
private fun openTaskForm(task: Task) {
|
||||
if (Date().time - (TasksFragment.lastTaskFormOpen?.time
|
||||
?: 0) < 2000 || !task.isValid || !canEditTasks
|
||||
?: 0) < 2000 || !task.isValid
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
|
@ -565,7 +563,7 @@ private fun openTaskForm(task: Task) {
|
|||
bundle.putString(TaskFormActivity.TASK_ID_KEY, task.id)
|
||||
bundle.putDouble(TaskFormActivity.TASK_VALUE_KEY, task.value)
|
||||
|
||||
if (task.canEdit(viewModel.userViewModel.userID)) {
|
||||
if (viewModel.canEditTask(task)) {
|
||||
MainNavigationController.navigate(R.id.taskFormActivity, bundle)
|
||||
} else {
|
||||
MainNavigationController.navigate(R.id.taskSummaryActivity, bundle)
|
||||
|
|
|
|||
Loading…
Reference in a new issue