diff --git a/Habitica/res/drawable-hdpi/assign.png b/Habitica/res/drawable-hdpi/assign.png new file mode 100644 index 000000000..7350842d7 Binary files /dev/null and b/Habitica/res/drawable-hdpi/assign.png differ diff --git a/Habitica/res/drawable-mdpi/assign.png b/Habitica/res/drawable-mdpi/assign.png new file mode 100644 index 000000000..20b6a498e Binary files /dev/null and b/Habitica/res/drawable-mdpi/assign.png differ diff --git a/Habitica/res/drawable-night-hdpi/assign.png b/Habitica/res/drawable-night-hdpi/assign.png new file mode 100644 index 000000000..df03c6189 Binary files /dev/null and b/Habitica/res/drawable-night-hdpi/assign.png differ diff --git a/Habitica/res/drawable-night-mdpi/assign.png b/Habitica/res/drawable-night-mdpi/assign.png new file mode 100644 index 000000000..dd618cb06 Binary files /dev/null and b/Habitica/res/drawable-night-mdpi/assign.png differ diff --git a/Habitica/res/drawable-night-xhdpi/assign.png b/Habitica/res/drawable-night-xhdpi/assign.png new file mode 100644 index 000000000..521040778 Binary files /dev/null and b/Habitica/res/drawable-night-xhdpi/assign.png differ diff --git a/Habitica/res/drawable-night-xxhdpi/assign.png b/Habitica/res/drawable-night-xxhdpi/assign.png new file mode 100644 index 000000000..b336e5fbe Binary files /dev/null and b/Habitica/res/drawable-night-xxhdpi/assign.png differ diff --git a/Habitica/res/drawable-xhdpi/assign.png b/Habitica/res/drawable-xhdpi/assign.png new file mode 100644 index 000000000..bb10ca5ef Binary files /dev/null and b/Habitica/res/drawable-xhdpi/assign.png differ diff --git a/Habitica/res/drawable-xxhdpi/assign.png b/Habitica/res/drawable-xxhdpi/assign.png new file mode 100644 index 000000000..bf0578c8c Binary files /dev/null and b/Habitica/res/drawable-xxhdpi/assign.png differ diff --git a/Habitica/res/layout/reward_item_card.xml b/Habitica/res/layout/reward_item_card.xml index af9d3a58e..ddfeb32f9 100644 --- a/Habitica/res/layout/reward_item_card.xml +++ b/Habitica/res/layout/reward_item_card.xml @@ -49,7 +49,7 @@ android:paddingBottom="2dp"/> 24dp 20dp - 10dp + 8dp 8dp 8dp 6dp 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 57bf84801..d7b66a56c 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 @@ -45,7 +45,15 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), .beginGroup() .equalTo("userId", ownerID) .or() + .beginGroup() .`in`("group.groupID", includedGroupIDs) + .and() + .beginGroup() + .contains("group.assignedUsers", ownerID) + .or() + .isEmpty("group.assignedUsers") + .endGroup() + .endGroup() .or() .equalTo("group.groupID", ownerID) .endGroup() @@ -129,14 +137,13 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), return taskList } - private fun removeOldTasks(userID: String, onlineTaskList: List) { - val groupIDs = onlineTaskList.map { it.group?.groupID }.distinct().toTypedArray() + private fun removeOldTasks(ownerID: String, onlineTaskList: List) { if (realm.isClosed) return val localTasks = realm.where(Task::class.java) .beginGroup() - .equalTo("userId", userID) + .equalTo("userId", ownerID) .or() - .`in`("group.groupID", groupIDs) + .equalTo("group.groupID", ownerID) .endGroup() .beginGroup() .beginGroup() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt deleted file mode 100644 index 4d97ad970..000000000 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/AssignedTextProvider.kt +++ /dev/null @@ -1,7 +0,0 @@ -package com.habitrpg.android.habitica.helpers - -import android.content.res.Resources - -interface AssignedTextProvider { - fun textForTask(resources: Resources, assignedUsers: List): String -} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/GroupPlanInfoProvider.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/GroupPlanInfoProvider.kt new file mode 100644 index 000000000..60d608130 --- /dev/null +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/helpers/GroupPlanInfoProvider.kt @@ -0,0 +1,10 @@ +package com.habitrpg.android.habitica.helpers + +import android.content.res.Resources +import com.habitrpg.android.habitica.models.tasks.Task + +interface GroupPlanInfoProvider { + fun assignedTextForTask(resources: Resources, assignedUsers: List): String + fun canScoreTask(task: Task): Boolean + fun canEditTask(task: Task): Boolean +} diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt index 7983d1261..cdd5ee55f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/adapter/tasks/RewardsRecyclerViewAdapter.kt @@ -96,7 +96,7 @@ class RewardsRecyclerViewAdapter( if (customRewards != null && position < customRewardCount) { val reward = customRewards?.get(position) ?: return val gold = user?.stats?.gp ?: 0.0 - (holder as? RewardViewHolder)?.isLocked = false + (holder as? RewardViewHolder)?.isLocked = !viewModel.canScoreTask(reward) (holder as? RewardViewHolder)?.bind(reward, position, reward.value <= gold, taskDisplayMode, viewModel.ownerID.value) } else if (inAppRewards != null) { val item = inAppRewards?.get(position - customRewardCount) ?: return @@ -122,7 +122,9 @@ class RewardsRecyclerViewAdapter( override fun getItemCount(): Int { var rewardCount = customRewardCount - rewardCount += inAppRewardCount + if (viewModel.isPersonalBoard) { + rewardCount += inAppRewardCount + } return rewardCount } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt index ac7d3c293..82854c32a 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/RewardsRecyclerviewFragment.kt @@ -16,15 +16,14 @@ import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.models.shops.ShopItem -import com.habitrpg.shared.habitica.models.tasks.TaskType import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar +import com.habitrpg.shared.habitica.models.tasks.TaskType import io.reactivex.rxjava3.functions.Consumer import kotlinx.coroutines.launch -import java.util.* class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() { @@ -45,7 +44,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() { (layoutManager as? GridLayoutManager)?.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() { override fun getSpanSize(position: Int): Int { - return if (recyclerAdapter?.getItemViewType(position) ?: 0 < 2) { + return if ((recyclerAdapter?.getItemViewType(position) ?: 0) < 2) { (layoutManager as? GridLayoutManager)?.spanCount ?: 1 } else { 1 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 f4552970b..6cf2c14f1 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 @@ -214,7 +214,6 @@ open class TaskRecyclerViewFragment : BaseFragment Unit), var openTaskFunc: ((Pair) -> Unit), var brokenTaskFunc: ((Task) -> Unit), - var assignedTextProvider: AssignedTextProvider? + var assignedTextProvider: GroupPlanInfoProvider? ) : BindableViewHolder(itemView), View.OnTouchListener { private val scope = MainScope() @@ -254,7 +254,7 @@ abstract class BaseTaskViewHolder constructor( } if (data.group?.assignedUsers?.isNotEmpty() == true) { - assignedTextView.text = assignedTextProvider?.textForTask(context.resources, data.group?.assignedUsers ?: emptyList()) + assignedTextView.text = assignedTextProvider?.assignedTextForTask(context.resources, data.group?.assignedUsers ?: emptyList()) assignedTextView.visibility = View.VISIBLE } else { assignedTextView.visibility = View.GONE diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt index 211c293df..58b3a27ad 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/ChecklistedViewHolder.kt @@ -12,7 +12,7 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.habitrpg.android.habitica.R -import com.habitrpg.android.habitica.helpers.AssignedTextProvider +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.models.tasks.ChecklistItem import com.habitrpg.android.habitica.models.tasks.Task @@ -32,7 +32,7 @@ abstract class ChecklistedViewHolder( var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Pair) -> Unit), brokenTaskFunc: ((Task) -> Unit), - assignedTextProvider: AssignedTextProvider? + assignedTextProvider: GroupPlanInfoProvider? ) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) { private val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt index 26ab150a7..04a2147aa 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/DailyViewHolder.kt @@ -1,7 +1,7 @@ package com.habitrpg.android.habitica.ui.viewHolders.tasks import android.view.View -import com.habitrpg.android.habitica.helpers.AssignedTextProvider +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.models.tasks.ChecklistItem import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.shared.habitica.models.responses.TaskDirection @@ -15,7 +15,7 @@ class DailyViewHolder( scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Pair) -> Unit), brokenTaskFunc: ((Task) -> Unit), - assignedTextProvider: AssignedTextProvider? + assignedTextProvider: GroupPlanInfoProvider? ) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) { override val taskIconWrapperIsVisible: Boolean diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt index e31f0337d..b56939f23 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/HabitViewHolder.kt @@ -6,9 +6,8 @@ import android.widget.Button import android.widget.FrameLayout import android.widget.ImageView import androidx.core.content.ContextCompat -import androidx.lifecycle.MutableLiveData import com.habitrpg.android.habitica.R -import com.habitrpg.android.habitica.helpers.AssignedTextProvider +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.shared.habitica.models.responses.TaskDirection @@ -17,7 +16,7 @@ class HabitViewHolder( scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Pair) -> Unit), brokenTaskFunc: ((Task) -> Unit), - assignedTextProvider: AssignedTextProvider? + assignedTextProvider: GroupPlanInfoProvider? ) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) { private val btnPlusWrapper: FrameLayout = itemView.findViewById(R.id.btnPlusWrapper) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt index 06f778ecb..c083240b7 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/RewardViewHolder.kt @@ -4,12 +4,14 @@ import android.view.MotionEvent import android.view.View import androidx.core.content.ContextCompat import androidx.core.graphics.ColorUtils +import androidx.core.graphics.drawable.toDrawable import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.databinding.RewardItemCardBinding -import com.habitrpg.android.habitica.helpers.AssignedTextProvider +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.ui.ItemDetailDialog import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.common.habitica.extensions.dpToPx import com.habitrpg.common.habitica.helpers.NumberAbbreviator import com.habitrpg.shared.habitica.models.responses.TaskDirection @@ -18,8 +20,14 @@ class RewardViewHolder( scoreTaskFunc: ((Task, TaskDirection) -> Unit), openTaskFunc: ((Pair) -> Unit), brokenTaskFunc: ((Task) -> Unit), - assignedTextProvider: AssignedTextProvider? -) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) { + assignedTextProvider: GroupPlanInfoProvider? +) : BaseTaskViewHolder( + itemView, + scoreTaskFunc, + openTaskFunc, + brokenTaskFunc, + assignedTextProvider +) { private val binding = RewardItemCardBinding.bind(itemView) private val isItem: Boolean @@ -29,6 +37,7 @@ class RewardViewHolder( binding.buyButton.setOnClickListener { buyReward() } + binding.goldIcon.setImageBitmap(HabiticaIconsHelper.imageOfGold()) } override fun canContainMarkdown(): Boolean { @@ -67,25 +76,44 @@ class RewardViewHolder( this.task = reward streakTextView.visibility = View.GONE super.bind(reward, position, displayMode, ownerID) - binding.priceLabel.text = NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0) + binding.priceLabel.text = + NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0) if (isLocked) { - binding.goldIcon.setImageResource(R.drawable.task_lock) - binding.goldIcon.drawable.setTint(ContextCompat.getColor(context, R.color.reward_buy_button_text)) - binding.goldIcon.alpha = 1.0f - binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.reward_buy_button_text)) - binding.buyButton.setBackgroundColor(ContextCompat.getColor(context, R.color.reward_buy_button_bg)) + binding.priceLabel.setCompoundDrawablesWithIntrinsicBounds( + HabiticaIconsHelper.imageOfLocked( + ContextCompat.getColor(context, R.color.gray_1_30), 10, 12 + ).toDrawable(context.resources), null, null, null + ) + binding.priceLabel.compoundDrawablePadding = 2.dpToPx(context) } else { - binding.goldIcon.setImageBitmap(HabiticaIconsHelper.imageOfGold()) - if (canBuy) { - binding.goldIcon.alpha = 1.0f - binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.reward_buy_button_text)) - binding.buyButton.setBackgroundColor(ContextCompat.getColor(context, R.color.reward_buy_button_bg)) - } else { - binding.goldIcon.alpha = 0.6f - binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.text_quad)) - binding.buyButton.setBackgroundColor(ColorUtils.setAlphaComponent(ContextCompat.getColor(context, R.color.offset_background), 127)) - } + binding.priceLabel.setCompoundDrawables(null, null, null, null) + } + if (canBuy && !isLocked) { + binding.goldIcon.alpha = 1.0f + binding.priceLabel.setTextColor( + ContextCompat.getColor( + context, + R.color.reward_buy_button_text + ) + ) + binding.buyButton.setBackgroundColor( + ContextCompat.getColor( + context, + R.color.reward_buy_button_bg + ) + ) + } else { + binding.goldIcon.alpha = 0.6f + binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.text_quad)) + binding.buyButton.setBackgroundColor( + ColorUtils.setAlphaComponent( + ContextCompat.getColor( + context, + R.color.offset_background + ), 127 + ) + ) } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt index b7bf10b2a..2370a4ad4 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewHolders/tasks/TodoViewHolder.kt @@ -1,7 +1,7 @@ package com.habitrpg.android.habitica.ui.viewHolders.tasks import android.view.View -import com.habitrpg.android.habitica.helpers.AssignedTextProvider +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.models.tasks.ChecklistItem import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.shared.habitica.models.responses.TaskDirection @@ -13,7 +13,7 @@ class TodoViewHolder( scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit), openTaskFunc: ((Pair) -> Unit), brokenTaskFunc: ((Task) -> Unit), - assignedTextProvider: AssignedTextProvider? + assignedTextProvider: GroupPlanInfoProvider? ) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) { private val dateFormatter: DateFormat = android.text.format.DateFormat.getDateFormat(context) 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 dcf766b42..c86726f30 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,6 +7,7 @@ import com.habitrpg.android.habitica.data.UserRepository import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.models.TeamPlan import com.habitrpg.android.habitica.models.invitations.PartyInvite +import com.habitrpg.android.habitica.models.members.Member import com.habitrpg.android.habitica.models.user.User import io.reactivex.rxjava3.disposables.CompositeDisposable import kotlinx.coroutines.ExperimentalCoroutinesApi @@ -43,10 +44,12 @@ class MainUserViewModel(private val providedUserID: String, val userRepository: .filterNotNull() .distinctUntilChanged { old, new -> old.id == new.id } .flatMapLatest { socialRepository.getGroup(it.id) } - var currentTeamPlanMembers = currentTeamPlan + @OptIn(ExperimentalCoroutinesApi::class) + var currentTeamPlanMembers: LiveData> = currentTeamPlan .filterNotNull() .distinctUntilChanged { old, new -> old.id == new.id } .flatMapLatest { socialRepository.getGroupMembers(it.id) } + .asLiveData() fun onCleared() { userRepository.close() 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 fd8850c0c..85a5af08b 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 @@ -12,8 +12,8 @@ import com.habitrpg.android.habitica.data.TagRepository 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.AssignedTextProvider import com.habitrpg.android.habitica.helpers.ExceptionHandler +import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider import com.habitrpg.android.habitica.models.TeamPlan import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.shared.habitica.models.responses.TaskDirection @@ -28,7 +28,7 @@ import kotlinx.coroutines.launch import java.util.Date import javax.inject.Inject -class TasksViewModel : BaseViewModel(), AssignedTextProvider { +class TasksViewModel : BaseViewModel(), GroupPlanInfoProvider { private var compositeSubscription: CompositeDisposable = CompositeDisposable() override fun inject(component: UserComponent) { @@ -305,14 +305,21 @@ class TasksViewModel : BaseViewModel(), AssignedTextProvider { return query } - fun canScoreTask(item: Task): Boolean { - if (!item.isGroupTask) { + override fun canScoreTask(task: Task): Boolean { + if (!task.isGroupTask) { return true } - return item.isAssignedToUser(userViewModel.userID) || item.group?.assignedUsers?.isEmpty() != false + return task.isAssignedToUser(userViewModel.userID) || task.group?.assignedUsers?.isEmpty() != false } - override fun textForTask(resources: Resources, assignedUsers: List): String { + override fun canEditTask(task: Task): Boolean { + if (!task.isGroupTask) { + return true + } + return false + } + + override fun assignedTextForTask(resources: Resources, assignedUsers: List): String { return if (assignedUsers.contains(userViewModel.userID)) { if (assignedUsers.size == 1) { resources.getString(R.string.you) @@ -320,7 +327,11 @@ class TasksViewModel : BaseViewModel(), AssignedTextProvider { resources.getQuantityString(R.plurals.you_x_others, assignedUsers.size - 1, assignedUsers.size - 1) } } else { - resources.getQuantityString(R.plurals.people, assignedUsers.size, assignedUsers.size) + if (assignedUsers.size == 1) { + userViewModel.currentTeamPlanMembers.value?.firstOrNull { it.id == assignedUsers.first() }?.displayName ?: "" + } else { + resources.getQuantityString(R.plurals.people, assignedUsers.size, assignedUsers.size) + } } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt index efcd3b6aa..6d7a86ca8 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt @@ -112,7 +112,7 @@ fun AppHeaderView( ) { val user by viewModel.user.observeAsState(null) val teamPlan by viewModel.currentTeamPlan.collectAsState(null) - val teamPlanMembers by viewModel.currentTeamPlanMembers.collectAsState(null) + val teamPlanMembers by viewModel.currentTeamPlanMembers.observeAsState() Column { Row { ComposableAvatarView( @@ -207,7 +207,10 @@ fun AppHeaderView( } ) { for (member in teamPlanMembers?.filter { it.id != user?.id }?.take(6) ?: emptyList()) { - Box(modifier = Modifier.clip(CircleShape).size(26.dp).padding(end = 6.dp, top = 4.dp)) { + Box(modifier = Modifier + .clip(CircleShape) + .size(26.dp) + .padding(end = 6.dp, top = 4.dp)) { ComposableAvatarView( avatar = member, Modifier diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt index d7e94af1f..03e3d67fd 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/navigation/HabiticaBottomNavigationView.kt @@ -5,7 +5,6 @@ import android.content.Context import android.graphics.PorterDuff import android.util.AttributeSet import android.view.MotionEvent -import android.view.View import android.view.animation.Animation import android.view.animation.BounceInterpolator import android.view.animation.LinearInterpolator @@ -68,28 +67,11 @@ class HabiticaBottomNavigationView @JvmOverloads constructor( field = value val animator = ObjectAnimator.ofFloat(0f, 1.0f) if (field) { - val params = binding.cutoutFill.layoutParams - // add some additional height because otherwise there is a weird white line - params.height = binding.cutoutBackground.height + 10 - binding.cutoutFill.layoutParams = params - animator.addUpdateListener { - val reversed = 1.0f - it.animatedFraction - binding.cutoutFill.translationY = -(reversed) * binding.cutoutBackground.height - } - binding.cutoutSpace.visibility = View.VISIBLE binding.addButtonBackground.animate() .translationY(0f) .alpha(1f) .setDuration(200) } else { - val params = binding.cutoutFill.layoutParams - // add some additional height because otherwise there is a weird white line - params.height = binding.cutoutBackground.height + 10 - binding.cutoutFill.layoutParams = params - animator.addUpdateListener { - binding.cutoutFill.translationY = -it.animatedFraction * (binding.cutoutBackground.height) - } - binding.cutoutSpace.visibility = View.INVISIBLE binding.addButtonBackground.animate() .translationY(-binding.addButtonBackground.height.toFloat() / 2) .alpha(0.0f) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt index 8a3455f0d..d6624df74 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/stats/StatsView.kt @@ -5,13 +5,126 @@ import android.graphics.PorterDuff import android.util.AttributeSet import android.view.View import android.widget.LinearLayout +import androidx.compose.animation.AnimatedVisibility +import androidx.compose.foundation.Image +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.fillMaxHeight +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.Text +import androidx.compose.material.TextButton +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.asImageBitmap +import androidx.compose.ui.res.colorResource +import androidx.compose.ui.res.stringResource +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 com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.databinding.StatsViewBinding -import com.habitrpg.common.habitica.extensions.layoutInflater import com.habitrpg.android.habitica.extensions.setTintWith import com.habitrpg.android.habitica.helpers.HapticFeedbackManager import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper +import com.habitrpg.common.habitica.extensions.layoutInflater + +@Composable +fun StatsViewComposable( + statText: String, + statColor: Color, + levelValue: Int, + equipmentValue: Int, + buffValue: Int, + allocatedValue: Int, + canAllocate: Boolean, + allocateAction: () -> Unit +) { + Column( + Modifier + .background(colorResource(R.color.window_background)) + .clip(RoundedCornerShape(12.dp))) { + Row( + Modifier + .height(43.dp) + .fillMaxWidth() + .background(statColor) + .padding(horizontal = 12.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically) { + Text(statText, color = colorResource(R.color.white)) + Text("${levelValue + equipmentValue + buffValue + allocatedValue}", color = colorResource(R.color.white)) + } + Row(Modifier.height(61.dp), + horizontalArrangement = Arrangement.SpaceEvenly, + verticalAlignment = Alignment.CenterVertically ) { + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.Center, Alignment.CenterHorizontally) { + Text(text = "$levelValue", fontSize = 20.sp) + Text(text = stringResource(R.string.level), color = colorResource(R.color.text_quad), fontSize = 12.sp) + } + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.Center, Alignment.CenterHorizontally) { + Text(text = "$equipmentValue", fontSize = 20.sp) + Text(text = stringResource(R.string.sidebar_equipment), color = colorResource(R.color.text_quad), fontSize = 12.sp) + } + Column(modifier = Modifier.weight(1f), verticalArrangement = Arrangement.Center, Alignment.CenterHorizontally) { + Text(text = "$buffValue", fontSize = 20.sp) + Text(text = stringResource(R.string.buffs), color = colorResource(R.color.text_quad), fontSize = 12.sp) + } + Column(modifier = Modifier + .weight(1f) + .fillMaxHeight() + .background(colorResource(if (canAllocate) R.color.offset_background_30 else R.color.window_background)), verticalArrangement = Arrangement.Center, Alignment.CenterHorizontally) { + Text(text = "$allocatedValue", fontSize = 20.sp, color = if (canAllocate) statColor else colorResource(R.color.text_primary)) + Text(text = stringResource(R.string.allocated), color = if (canAllocate) statColor else colorResource(R.color.text_quad), fontSize = 12.sp) + } + AnimatedVisibility(visible = canAllocate) { + TextButton(onClick = allocateAction, + Modifier + .width(48.dp) + .fillMaxHeight() + .background( + colorResource(id = R.color.offset_background_30) + )) { + Image(HabiticaIconsHelper.imageOfAttributeAllocateButton().asImageBitmap(), null) + } + } + } + } +} + +@Preview +@Composable +fun StatsViewPreview() { + Column(Modifier.background(colorResource(id = R.color.content_background))) { + StatsViewComposable( + statText = "Strength", + statColor = colorResource(id = R.color.red_50), + levelValue = 10, + equipmentValue = 5, + buffValue = 4, + allocatedValue = 8, + canAllocate = false + ) {} + StatsViewComposable( + statText = "Intelligence", + statColor = colorResource(id = R.color.blue_50), + levelValue = 10, + equipmentValue = 5, + buffValue = 4, + allocatedValue = 8, + canAllocate = true + ) {} + } +} class StatsView(context: Context, attrs: AttributeSet?) : LinearLayout(context, attrs) { private val binding = StatsViewBinding.inflate(context.layoutInflater, this, true) diff --git a/common/src/main/res/values/colors.xml b/common/src/main/res/values/colors.xml index 994f82a89..1488f6ea8 100644 --- a/common/src/main/res/values/colors.xml +++ b/common/src/main/res/values/colors.xml @@ -73,6 +73,8 @@ #edecee #f9f9f9 + #4D1A181D + #033f5e #005158 #005737 diff --git a/version.properties b/version.properties index ce8608982..be277252e 100644 --- a/version.properties +++ b/version.properties @@ -1,2 +1,2 @@ NAME=4.0.3 -CODE=4571 \ No newline at end of file +CODE=4581 \ No newline at end of file