From a7e3e11526efaf851fa21660ddea134ea2fcdd70 Mon Sep 17 00:00:00 2001 From: Phillip Thelen Date: Tue, 16 Aug 2022 10:56:04 +0200 Subject: [PATCH] Improve group task display --- Habitica/res/values/strings.xml | 7 +++-- .../android/habitica/data/TaskRepository.kt | 8 +++--- .../data/implementation/TaskRepositoryImpl.kt | 8 +++--- .../data/local/TaskLocalRepository.kt | 6 ++--- .../RealmTaskLocalRepository.kt | 24 ++++++++++------- .../habitica/helpers/TaskAlarmManager.kt | 8 +++--- .../android/habitica/models/tasks/Task.kt | 4 +++ .../habitica/models/user/Preferences.kt | 1 + .../models/user/UserTaskPreferences.kt | 13 ++++++++++ .../receivers/NotificationPublisher.kt | 4 +-- .../habitica/ui/activities/ArmoireActivity.kt | 2 +- .../activities/HabitButtonWidgetActivity.kt | 4 +-- .../habitica/ui/fragments/BaseMainFragment.kt | 4 +-- .../skills/SkillTasksRecyclerViewFragment.kt | 12 ++++----- .../tasks/TaskRecyclerViewFragment.kt | 3 ++- .../ui/fragments/tasks/TasksFragment.kt | 8 +++--- .../ui/viewmodels/MainUserViewModel.kt | 12 +++++++-- .../habitica/ui/viewmodels/TasksViewModel.kt | 10 +++---- .../views/yesterdailies/YesterdailyDialog.kt | 8 +++--- .../android/habitica/utils/TaskSerializer.kt | 16 +++++++++--- .../widget/AvatarStatsWidgetProvider.kt | 26 ++++++++++++------- .../habitica/widget/TaskListFactory.kt | 4 +-- version.properties | 2 +- 23 files changed, 119 insertions(+), 75 deletions(-) create mode 100644 Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserTaskPreferences.kt diff --git a/Habitica/res/values/strings.xml b/Habitica/res/values/strings.xml index 50125ea84..5621d5174 100644 --- a/Habitica/res/values/strings.xml +++ b/Habitica/res/values/strings.xml @@ -265,10 +265,9 @@ You purchased a reward World Quest Need a break? Check into Daniel’s Inn to pause some of Habitica’s more difficult game mechanics:\n\n -• Missed Dailies won’t damage you\n -• Tasks won’t lose streaks or decay in color\n -• Bosses won’t do damage for your missed Dailies\n -• Your boss damage or collection quest items will stay pending until check-out +• Your missed Dailies won\'t damage you (bosses will still do damage caused by other Party member\'s missed Dailies) +• Your Task streaks and Habit counters will not reset +• Your damage to the Quest boss or found collection items will remain pending until you check out of the Inn You don\'t have any %s Lvl %1$d %2$s Level %1$d %2$s diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt index 99ca776a5..b6df6db47 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/TaskRepository.kt @@ -2,12 +2,12 @@ package com.habitrpg.android.habitica.data import com.habitrpg.android.habitica.models.BaseMainObject import com.habitrpg.android.habitica.models.responses.BulkTaskScoringData -import com.habitrpg.common.habitica.models.responses.TaskScoringResult import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.tasks.TaskList +import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.common.habitica.models.responses.TaskScoringResult import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.common.habitica.models.tasks.TasksOrder -import com.habitrpg.android.habitica.models.user.User import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import io.reactivex.rxjava3.core.Single @@ -15,8 +15,8 @@ import kotlinx.coroutines.flow.Flow import java.util.Date interface TaskRepository : BaseRepository { - fun getTasks(taskType: TaskType, userID: String? = null): Flow> - fun getTasksFlowable(taskType: TaskType, userID: String? = null): Flowable> + fun getTasks(taskType: TaskType, userID: String? = null, includedGroupIDs: Array): Flow> + fun getTasksFlowable(taskType: TaskType, userID: String? = null, includedGroupIDs: Array): Flowable> fun saveTasks(userId: String, order: TasksOrder, tasks: TaskList) fun retrieveTasks(userId: String, tasksOrder: TasksOrder): Flowable diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt index 66fe6a6ed..4822e1ced 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/TaskRepositoryImpl.kt @@ -39,11 +39,11 @@ class TaskRepositoryImpl( ) : BaseRepositoryImpl(localRepository, apiClient, userID), TaskRepository { private var lastTaskAction: Long = 0 - override fun getTasks(taskType: TaskType, userID: String?): Flow> = - this.localRepository.getTasks(taskType, userID ?: this.userID) + override fun getTasks(taskType: TaskType, userID: String?, includedGroupIDs: Array): Flow> = + this.localRepository.getTasks(taskType, userID ?: this.userID, includedGroupIDs) - override fun getTasksFlowable(taskType: TaskType, userID: String?): Flowable> = - this.localRepository.getTasksFlowable(taskType, userID ?: this.userID) + override fun getTasksFlowable(taskType: TaskType, userID: String?, includedGroupIDs: Array): Flowable> = + this.localRepository.getTasksFlowable(taskType, userID ?: this.userID, includedGroupIDs) override fun saveTasks(userId: String, order: TasksOrder, tasks: TaskList) { localRepository.saveTasks(userId, order, tasks) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt index 5099302cf..b8e1c1692 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/TaskLocalRepository.kt @@ -2,17 +2,17 @@ package com.habitrpg.android.habitica.data.local import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.tasks.TaskList +import com.habitrpg.android.habitica.models.user.User import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.common.habitica.models.tasks.TasksOrder -import com.habitrpg.android.habitica.models.user.User import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Maybe import kotlinx.coroutines.flow.Flow interface TaskLocalRepository : BaseLocalRepository { - fun getTasks(taskType: TaskType, userID: String): Flow> - fun getTasksFlowable(taskType: TaskType, userID: String): Flowable> + fun getTasks(taskType: TaskType, userID: String, includedGroupIDs: Array): Flow> + fun getTasksFlowable(taskType: TaskType, userID: String, includedGroupIDs: Array): Flowable> fun getTasks(userId: String): Flow> fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt index a97d3bb8e..0803d8525 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTaskLocalRepository.kt @@ -21,22 +21,30 @@ import kotlinx.coroutines.flow.filter class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), TaskLocalRepository { - override fun getTasks(taskType: TaskType, userID: String): Flow> { + override fun getTasks(taskType: TaskType, userID: String, includedGroupIDs: Array): Flow> { if (realm.isClosed) return emptyFlow() return realm.where(Task::class.java) .equalTo("typeValue", taskType.value) + .beginGroup() .equalTo("userId", userID) + .or() + .`in`("group.groupID", includedGroupIDs) + .endGroup() .sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING) .findAll() .toFlow() .filter { it.isLoaded } } - override fun getTasksFlowable(taskType: TaskType, userID: String): Flowable> { + override fun getTasksFlowable(taskType: TaskType, userID: String, includedGroupIDs: Array): Flowable> { if (realm.isClosed) return Flowable.empty() return RxJavaBridge.toV3Flowable(realm.where(Task::class.java) .equalTo("typeValue", taskType.value) + .beginGroup() .equalTo("userId", userID) + .or() + .`in`("group.groupID", includedGroupIDs) + .endGroup() .sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING) .findAll() .asFlowable() @@ -53,17 +61,20 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), } override fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList) { - val sortedTasks = ArrayList() + val sortedTasks = mutableListOf() sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.habits)) sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.dailys)) sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.todos)) sortedTasks.addAll(sortTasks(tasks.tasks, tasksOrder.rewards)) + for (task in tasks.tasks.values) { + task.position = (sortedTasks.lastOrNull { it.type == task.type }?.position ?: -1) + 1 + sortedTasks.add(task) + } removeOldTasks(ownerID, sortedTasks) val allChecklistItems = ArrayList() val allReminders = ArrayList() sortedTasks.forEach { - if (it.userId.isBlank()) it.userId = ownerID it.checklist?.let { it1 -> allChecklistItems.addAll(it1) } it.reminders?.let { it1 -> allReminders.addAll(it1) } } @@ -110,11 +121,6 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), taskMap.remove(taskId) } } - for (task in taskMap.values) { - task.position = position - taskList.add(task) - position++ - } return taskList } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt index b812eb85f..6dfe805e3 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/TaskAlarmManager.kt @@ -11,9 +11,9 @@ import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.extensions.withImmutableFlag import com.habitrpg.android.habitica.models.tasks.RemindersItem import com.habitrpg.android.habitica.models.tasks.Task -import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.android.habitica.receivers.NotificationPublisher import com.habitrpg.android.habitica.receivers.TaskReceiver +import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.shared.habitica.HLogger import com.habitrpg.shared.habitica.LogLevel import kotlinx.coroutines.flow.firstOrNull @@ -91,7 +91,7 @@ class TaskAlarmManager( */ private fun setAlarmForRemindersItem(reminderItemTask: Task, remindersItem: RemindersItem?) { val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant() - if (remindersItem == null || (remindersItem.getLocalZonedDateTimeInstant()?.isBefore(now) == true && reminderItemTask.nextDue?.first() != null)) { + if (remindersItem == null || (remindersItem.getLocalZonedDateTimeInstant()?.isBefore(now) == true && reminderItemTask.nextDue?.firstOrNull() != null)) { return } @@ -104,7 +104,7 @@ class TaskAlarmManager( intent.putExtra(TASK_NAME_INTENT_KEY, reminderItemTask.text) intent.putExtra(TASK_ID_INTENT_KEY, reminderItemTask.id) - val intentId = remindersItem.id?.hashCode() ?: 0 and 0xfffffff + val intentId = remindersItem.id?.hashCode() ?: (0 and 0xfffffff) // Cancel alarm if already exists val previousSender = PendingIntent.getBroadcast( context, @@ -130,7 +130,7 @@ class TaskAlarmManager( private fun removeAlarmForRemindersItem(remindersItem: RemindersItem) { val intent = Intent(context, TaskReceiver::class.java) intent.action = remindersItem.id - val intentId = remindersItem.id?.hashCode() ?: 0 and 0xfffffff + val intentId = remindersItem.id?.hashCode() ?: (0 and 0xfffffff) val sender = PendingIntent.getBroadcast( context, intentId, 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 49ac434a6..8ce4f6911 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 @@ -195,6 +195,10 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask { val isGroupTask: Boolean get() = group?.groupID?.isNotBlank() == true + fun isAssignedToUser(userID: String): Boolean { + return group?.assignedUsers?.contains(userID) == true + } + val isPendingApproval: Boolean get() = (group?.approvalRequired == true && group?.approvalRequested == true && group?.approvalApproved == false) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt index 38df6c5d9..d8a35787c 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/Preferences.kt @@ -40,4 +40,5 @@ open class Preferences : RealmObject(), AvatarPreferences, BaseObject { var pushNotifications: PushNotificationsPreference? = null var emailNotifications: EmailNotificationsPreference? = null var autoEquip: Boolean = true + var tasks: UserTaskPreferences? = null } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserTaskPreferences.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserTaskPreferences.kt new file mode 100644 index 000000000..0be0ed8c0 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/user/UserTaskPreferences.kt @@ -0,0 +1,13 @@ +package com.habitrpg.android.habitica.models.user + +import com.habitrpg.android.habitica.models.BaseObject +import io.realm.RealmList +import io.realm.RealmObject +import io.realm.annotations.RealmClass + +@RealmClass(embedded = true) +open class UserTaskPreferences: RealmObject(), BaseObject { + var confirmScoreNotes: Boolean = false + var mirrorGroupTasks: RealmList = RealmList() + var groupByChallenge: Boolean = false +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.kt index 4de2a5f5f..8e87b3152 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/receivers/NotificationPublisher.kt @@ -18,9 +18,9 @@ import com.habitrpg.android.habitica.extensions.withImmutableFlag import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.helpers.TaskAlarmManager import com.habitrpg.android.habitica.models.tasks.Task -import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.ui.activities.MainActivity +import com.habitrpg.common.habitica.models.tasks.TaskType import io.reactivex.rxjava3.functions.BiFunction import java.util.Calendar import java.util.Date @@ -58,7 +58,7 @@ class NotificationPublisher : BroadcastReceiver() { } val checkDailies = intent.getBooleanExtra(CHECK_DAILIES, false) if (checkDailies) { - taskRepository.getTasksFlowable(TaskType.DAILY).firstElement().zipWith( + taskRepository.getTasksFlowable(TaskType.DAILY, null, emptyArray()).firstElement().zipWith( userRepository.getUserFlowable().firstElement(), BiFunction, User, Pair, User>> { tasks, user -> return@BiFunction Pair(tasks, user) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt index 56a0080a2..04bd9ce07 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ArmoireActivity.kt @@ -65,7 +65,7 @@ class ArmoireActivity : BaseActivity() { userViewModel.user.observeOnce(this) { user -> gold = user?.stats?.gp - val remaining = inventoryRepository.getArmoireRemainingCount() - 1 + val remaining = inventoryRepository.getArmoireRemainingCount() binding.equipmentCountView.text = getString(R.string.equipment_remaining, remaining) binding.noEquipmentView.visibility = if (remaining > 0) View.GONE else View.VISIBLE } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt index d2910c416..9278a6dcc 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/HabitButtonWidgetActivity.kt @@ -13,10 +13,10 @@ import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.databinding.WidgetConfigureHabitButtonBinding import com.habitrpg.android.habitica.helpers.RxErrorHandler -import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.android.habitica.modules.AppModule import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider +import com.habitrpg.common.habitica.models.tasks.TaskType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -82,7 +82,7 @@ class HabitButtonWidgetActivity : BaseActivity() { binding.recyclerView.adapter = adapter CoroutineScope(Dispatchers.Main + job).launch { - adapter?.data = taskRepository.getTasks(TaskType.HABIT, userId).firstOrNull() ?: listOf() + adapter?.data = taskRepository.getTasks(TaskType.HABIT, userId, emptyArray()).firstOrNull() ?: listOf() } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt index 6402d7999..f1a69cc4f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseMainFragment.kt @@ -17,11 +17,11 @@ import com.google.firebase.analytics.FirebaseAnalytics import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.ApiClient import com.habitrpg.android.habitica.data.UserRepository -import com.habitrpg.common.habitica.extensions.getThemeColor import com.habitrpg.android.habitica.extensions.setScaledPadding import com.habitrpg.android.habitica.helpers.SoundManager import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.helpers.ToolbarColorHelper +import com.habitrpg.common.habitica.extensions.getThemeColor import javax.inject.Inject abstract class BaseMainFragment : BaseFragment() { @@ -96,7 +96,7 @@ abstract class BaseMainFragment : BaseFragment() { open fun updateToolbarInteractivity() { activity?.binding?.toolbarTitle?.background?.alpha = if (isTitleInteractive) 255 else 0 if (isTitleInteractive) { - activity?.binding?.toolbarTitle?.setScaledPadding(context, 16, 1, 16, 1) + activity?.binding?.toolbarTitle?.setScaledPadding(context, 16, 4, 16, 4) } else { activity?.binding?.toolbarTitle?.setPadding(0) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillTasksRecyclerViewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillTasksRecyclerViewFragment.kt index d2b5a4f28..104af9689 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillTasksRecyclerViewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillTasksRecyclerViewFragment.kt @@ -11,22 +11,21 @@ import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.tasks.Task -import com.habitrpg.common.habitica.models.tasks.TaskType -import com.habitrpg.android.habitica.modules.AppModule import com.habitrpg.android.habitica.ui.adapter.SkillTasksRecyclerViewAdapter import com.habitrpg.android.habitica.ui.fragments.BaseFragment +import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel +import com.habitrpg.common.habitica.models.tasks.TaskType import io.reactivex.rxjava3.core.BackpressureStrategy import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.subjects.PublishSubject import kotlinx.coroutines.flow.map import javax.inject.Inject -import javax.inject.Named class SkillTasksRecyclerViewFragment : BaseFragment() { @Inject lateinit var taskRepository: TaskRepository - @field:[Inject Named(AppModule.NAMED_USER_ID)] - lateinit var userId: String + @Inject + lateinit var userViewModel: MainUserViewModel var taskType: TaskType? = null override var binding: FragmentRecyclerviewBinding? = null @@ -65,7 +64,8 @@ class SkillTasksRecyclerViewFragment : BaseFragment ) binding?.recyclerView?.adapter = adapter - var tasks = taskRepository.getTasks(taskType ?: TaskType.HABIT) + val additionalGroupIDs = userViewModel.mirrorGroupTasks.toTypedArray() + var tasks = taskRepository.getTasks(taskType ?: TaskType.HABIT, userViewModel.userID, additionalGroupIDs) .map { it.filter { it.challengeID == null && it.group == null } } if (taskType == TaskType.TODO) { tasks = tasks.map { it.filter { !it.completed } } 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 eeee0b8e3..6752818ba 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 @@ -329,8 +329,9 @@ open class TaskRecyclerViewFragment : BaseFragment(), SearchView.O val taskTypeValue = args.taskType if (args.ownerID?.isNotBlank() == true) { viewModel.canSwitchOwners.value = false - viewModel.ownerID.value = args.ownerID ?: viewModel.userID + viewModel.ownerID.value = args.ownerID ?: viewModel.userViewModel.userID } else { - viewModel.ownerID.value = viewModel.userID + viewModel.ownerID.value = viewModel.userViewModel.userID } if (taskTypeValue?.isNotBlank() == true) { val taskType = TaskType.from(taskTypeValue) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt index 89a694e6b..3b29243c2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainUserViewModel.kt @@ -7,16 +7,22 @@ import com.habitrpg.android.habitica.data.UserRepository import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.invitations.PartyInvite import com.habitrpg.android.habitica.models.user.User +import com.habitrpg.android.habitica.modules.AppModule import io.reactivex.rxjava3.disposables.CompositeDisposable +import javax.inject.Inject +import javax.inject.Named class MainUserViewModel(val userRepository: UserRepository) { + @field:[Inject Named(AppModule.NAMED_USER_ID)] + lateinit var injectedUserID: String + val formattedUsername: CharSequence? get() = user.value?.formattedUsername val partyInvitations: List get() = user.value?.invitations?.parties ?: emptyList() - val userID: String? - get() = user.value?.id + val userID: String + get() = user.value?.id ?: injectedUserID val username: CharSequence get() = user.value?.username ?: "" val displayName: CharSequence @@ -27,6 +33,8 @@ class MainUserViewModel(val userRepository: UserRepository) { get() = (user.value?.stats?.hp ?: 1.0) == 0.0 val isUserInParty: Boolean get() = user.value?.hasParty == true + val mirrorGroupTasks: List + get() = user.value?.preferences?.tasks?.mirrorGroupTasks ?: emptyList() val user: LiveData diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt index a4f3a62c9..fd16b33ca 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/TasksViewModel.kt @@ -11,11 +11,10 @@ import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.helpers.AmplitudeManager import com.habitrpg.android.habitica.helpers.AppConfigManager import com.habitrpg.android.habitica.helpers.RxErrorHandler +import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.common.habitica.models.responses.TaskDirection import com.habitrpg.common.habitica.models.responses.TaskScoringResult -import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.common.habitica.models.tasks.TaskType -import com.habitrpg.android.habitica.modules.AppModule import io.reactivex.rxjava3.disposables.CompositeDisposable import io.realm.Case import io.realm.OrderedRealmCollection @@ -24,7 +23,6 @@ import io.realm.Sort import kotlinx.coroutines.launch import java.util.Date import javax.inject.Inject -import javax.inject.Named class TasksViewModel : BaseViewModel() { private var compositeSubscription: CompositeDisposable = CompositeDisposable() @@ -33,8 +31,6 @@ class TasksViewModel : BaseViewModel() { component.inject(this) } - @field:[Inject Named(AppModule.NAMED_USER_ID)] - lateinit var userID: String @Inject lateinit var taskRepository: TaskRepository @Inject @@ -53,7 +49,7 @@ class TasksViewModel : BaseViewModel() { val isPersonalBoard: Boolean get() { - return ownerID.value == userID + return ownerID.value == userViewModel.userID } val ownerTitle: CharSequence get() { @@ -65,7 +61,7 @@ class TasksViewModel : BaseViewModel() { viewModelScope.launch { userRepository.getTeamPlans() .collect { - owners = listOf(Pair(userID, userViewModel.displayName)) + it.map { + owners = listOf(Pair(userViewModel.userID, userViewModel.displayName)) + it.map { Pair( it.id, it.summary diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt index 132d5c68e..cc6047130 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/yesterdailies/YesterdailyDialog.kt @@ -12,14 +12,14 @@ import androidx.core.content.ContextCompat import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.data.UserRepository -import com.habitrpg.common.habitica.extensions.dpToPx -import com.habitrpg.common.habitica.extensions.isUsingNightModeResources import com.habitrpg.android.habitica.helpers.AmplitudeManager import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.tasks.ChecklistItem import com.habitrpg.android.habitica.models.tasks.Task -import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog +import com.habitrpg.common.habitica.extensions.dpToPx +import com.habitrpg.common.habitica.extensions.isUsingNightModeResources +import com.habitrpg.common.habitica.models.tasks.TaskType import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable import java.lang.ref.WeakReference @@ -226,7 +226,7 @@ class YesterdailyDialog private constructor( } .firstElement() .zipWith( - taskRepository.getTasksFlowable(TaskType.DAILY).firstElement() + taskRepository.getTasksFlowable(TaskType.DAILY, null, emptyArray()).firstElement() .map { val taskMap = mutableMapOf() it.forEachIndexed { index, task -> taskMap[task.id ?: ""] = index } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskSerializer.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskSerializer.kt index b4845d870..bea720fa2 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskSerializer.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/utils/TaskSerializer.kt @@ -5,6 +5,7 @@ import com.google.gson.JsonDeserializationContext import com.google.gson.JsonDeserializer import com.google.gson.JsonElement import com.google.gson.JsonObject +import com.google.gson.JsonPrimitive import com.google.gson.JsonSerializationContext import com.google.gson.JsonSerializer import com.habitrpg.android.habitica.extensions.getAsString @@ -20,6 +21,13 @@ import io.realm.RealmList import java.lang.reflect.Type import java.util.Date +private val JsonPrimitive.asBooleanOrFalse: Boolean + get() = if (isBoolean) { + asBoolean + } else { + false + } + class TaskSerializer : JsonSerializer, JsonDeserializer { private fun getIntListFromJsonArray(jsonArray: JsonArray): List { @@ -116,11 +124,11 @@ class TaskSerializer : JsonSerializer, JsonDeserializer { if (obj.has("group")) { val groupObject = obj.getAsJsonObject("group") val group: TaskGroupPlan = context.deserialize(groupObject, TaskGroupPlan::class.java) - if (group.groupID?.isNotBlank() == true) { + if (group.groupID?.isNotBlank() == true && groupObject.has("approval")) { val approvalObject = groupObject.getAsJsonObject("approval") - if (approvalObject.has("requested")) group.approvalRequested = approvalObject.getAsJsonPrimitive("requested").asBoolean - if (approvalObject.has("approved")) group.approvalApproved = approvalObject.getAsJsonPrimitive("approved").asBoolean - if (approvalObject.has("required")) group.approvalRequired = approvalObject.getAsJsonPrimitive("required").asBoolean + if (approvalObject.has("requested")) group.approvalRequested = approvalObject.getAsJsonPrimitive("requested").asBooleanOrFalse + if (approvalObject.has("approved")) group.approvalApproved = approvalObject.getAsJsonPrimitive("approved").asBooleanOrFalse + if (approvalObject.has("required")) group.approvalRequired = approvalObject.getAsJsonPrimitive("required").asBooleanOrFalse task.group = group } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt index 1cc60c14d..7e7118698 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/AvatarStatsWidgetProvider.kt @@ -5,29 +5,32 @@ import android.appwidget.AppWidgetManager import android.content.ComponentName import android.content.Context import android.content.Intent +import android.os.Build import android.view.View import android.view.ViewGroup import android.widget.RemoteViews import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.R -import com.habitrpg.common.habitica.extensions.dpToPx import com.habitrpg.android.habitica.extensions.withImmutableFlag -import com.habitrpg.common.habitica.helpers.HealthFormatter -import com.habitrpg.common.habitica.helpers.NumberAbbreviator import com.habitrpg.android.habitica.helpers.RxErrorHandler import com.habitrpg.android.habitica.models.user.User -import com.habitrpg.common.habitica.views.AvatarView import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.common.habitica.extensions.dpToPx +import com.habitrpg.common.habitica.helpers.HealthFormatter +import com.habitrpg.common.habitica.helpers.NumberAbbreviator +import com.habitrpg.common.habitica.views.AvatarView class AvatarStatsWidgetProvider : BaseWidgetProvider() { + private lateinit var avatarView: AvatarView private var user: User? = null private var appWidgetManager: AppWidgetManager? = null private var showManaBar: Boolean = true private var showAvatar: Boolean = true + override fun layoutResourceId(): Int { return R.layout.widget_avatar_stats } @@ -41,6 +44,10 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { override fun onEnabled(context: Context) { super.onEnabled(context) + avatarView = AvatarView(context, showBackground = true, showMount = true, showPet = true) + val layoutParams = ViewGroup.LayoutParams(140.dpToPx(context), 147.dpToPx(context)) + avatarView.layoutParams = layoutParams + this.setUp() userRepository.getUserFlowable().subscribe({ user = it @@ -54,6 +61,11 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { appWidgetIds: IntArray ) { super.onUpdate(context, appWidgetManager, appWidgetIds) + if (!this::avatarView.isInitialized) { + avatarView = AvatarView(context, showBackground = true, showMount = true, showPet = true) + val layoutParams = ViewGroup.LayoutParams(140.dpToPx(context), 147.dpToPx(context)) + avatarView.layoutParams = layoutParams + } this.setUp() this.appWidgetManager = appWidgetManager this.context = context @@ -74,7 +86,7 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { columns: Int, rows: Int ): RemoteViews { - showAvatar = columns > 3 + showAvatar = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) columns > 4 else columns > 3 if (showAvatar) { remoteViews.setViewVisibility(R.id.avatar_view, View.VISIBLE) } else { @@ -141,10 +153,6 @@ class AvatarStatsWidgetProvider : BaseWidgetProvider() { remoteViews.setTextViewText(R.id.lvl_tv, context.getString(R.string.user_level, user.stats?.lvl ?: 0)) if (showAvatar) { - val avatarView = - AvatarView(context, showBackground = true, showMount = true, showPet = true) - val layoutParams = ViewGroup.LayoutParams(140.dpToPx(context), 147.dpToPx(context)) - avatarView.layoutParams = layoutParams avatarView.setAvatar(user) val finalRemoteViews = remoteViews avatarView.onAvatarImageReady { bitmap -> diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt index 5fb927d14..026d925e8 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/widget/TaskListFactory.kt @@ -11,8 +11,8 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.data.TaskRepository import com.habitrpg.android.habitica.data.UserRepository import com.habitrpg.android.habitica.models.tasks.Task -import com.habitrpg.common.habitica.models.tasks.TaskType import com.habitrpg.common.habitica.helpers.MarkdownParser +import com.habitrpg.common.habitica.models.tasks.TaskType import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.SupervisorJob @@ -46,7 +46,7 @@ abstract class TaskListFactory internal constructor( return } CoroutineScope(Dispatchers.Main + job).launch { - val tasks = taskRepository.getTasks(taskType).firstOrNull()?.filter { task -> + val tasks = taskRepository.getTasks(taskType, null, emptyArray()).firstOrNull()?.filter { task -> task.type == TaskType.TODO && !task.completed || task.isDisplayedActive } ?: return@launch taskList = taskRepository.getTaskCopies(tasks) diff --git a/version.properties b/version.properties index 9118e3a28..2bdfb8477 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ NAME=4.0.1 -CODE=4360 \ No newline at end of file +CODE=4370 \ No newline at end of file