mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
So many fixes
This commit is contained in:
parent
6791b1e065
commit
442ea640ca
24 changed files with 223 additions and 102 deletions
|
|
@ -5,14 +5,14 @@ import androidx.appcompat.app.AppCompatActivity
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.executors.PostExecutionThread
|
||||
import com.habitrpg.android.habitica.helpers.SoundManager
|
||||
import com.habitrpg.common.habitica.models.responses.TaskScoringResult
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.common.habitica.models.responses.TaskScoringResult
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.GlobalScope
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
class DisplayItemDropUseCase @Inject
|
||||
constructor(private val soundManager: SoundManager, postExecutionThread: PostExecutionThread) : UseCase<DisplayItemDropUseCase.RequestValues, Void>(postExecutionThread) {
|
||||
|
|
@ -22,14 +22,14 @@ constructor(private val soundManager: SoundManager, postExecutionThread: PostExe
|
|||
val data = requestValues.data
|
||||
val snackbarText = StringBuilder(data?.drop?.dialog ?: "")
|
||||
|
||||
if (data?.questItemsFound ?: 0 > 0 && requestValues.showQuestItems) {
|
||||
if ((data?.questItemsFound ?: 0) > 0 && requestValues.showQuestItems) {
|
||||
if (snackbarText.isNotEmpty())
|
||||
snackbarText.append('\n')
|
||||
snackbarText.append(requestValues.context.getString(R.string.quest_items_found, data!!.questItemsFound))
|
||||
}
|
||||
|
||||
if (snackbarText.isNotEmpty()) {
|
||||
GlobalScope.launch(context = Dispatchers.Main) {
|
||||
MainScope().launch(context = Dispatchers.Main) {
|
||||
delay(3000L)
|
||||
HabiticaSnackbar.showSnackbar(
|
||||
requestValues.snackbarTargetView,
|
||||
|
|
|
|||
|
|
@ -8,7 +8,6 @@ import androidx.fragment.app.DialogFragment
|
|||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ItemItemBinding
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.android.habitica.models.inventory.Egg
|
||||
import com.habitrpg.android.habitica.models.inventory.Food
|
||||
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
|
||||
|
|
@ -20,10 +19,11 @@ import com.habitrpg.android.habitica.models.user.OwnedItem
|
|||
import com.habitrpg.android.habitica.models.user.OwnedPet
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.android.habitica.ui.menu.BottomSheetMenu
|
||||
import com.habitrpg.android.habitica.ui.menu.BottomSheetMenuItem
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.DetailDialog
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import io.reactivex.rxjava3.core.BackpressureStrategy
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.subjects.PublishSubject
|
||||
|
|
@ -105,7 +105,7 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter<OwnedI
|
|||
hatchingItem?.key + "-" + item?.key
|
||||
}
|
||||
val pet = existingPets?.firstOrNull { it.key == petKey && it.type != "special" }
|
||||
return pet != null && ownedPets?.get(pet.key)?.trained ?: 0 <= 0
|
||||
return pet != null && (ownedPets?.get(pet.key)?.trained ?: 0) <= 0
|
||||
}
|
||||
|
||||
init {
|
||||
|
|
@ -179,9 +179,9 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter<OwnedI
|
|||
}
|
||||
} else if (item is SpecialItem) {
|
||||
val specialItem = item as SpecialItem
|
||||
if (specialItem.isMysteryItem && ownedItem?.numberOwned ?: 0 > 0) {
|
||||
if (specialItem.isMysteryItem && (ownedItem?.numberOwned ?: 0) > 0) {
|
||||
menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.open)))
|
||||
} else if (ownedItem?.numberOwned ?: 0 > 0) {
|
||||
} else if ((ownedItem?.numberOwned ?: 0) > 0) {
|
||||
menu.addMenuItem(BottomSheetMenuItem(resources.getString(R.string.use_item)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
package com.habitrpg.common.habitica.models.responses
|
||||
|
||||
import android.os.Parcel
|
||||
import android.os.Parcelable
|
||||
|
||||
class TaskDirectionDataTemp {
|
||||
|
||||
var drop: TaskDirectionDataDrop? = null
|
||||
|
|
@ -12,10 +15,37 @@ class TaskDirectionDataQuest {
|
|||
var collection: Int = 0
|
||||
}
|
||||
|
||||
class TaskDirectionDataDrop {
|
||||
|
||||
class TaskDirectionDataDrop() : Parcelable {
|
||||
var value: Int = 0
|
||||
var key: String? = null
|
||||
var type: String? = null
|
||||
var dialog: String? = null
|
||||
|
||||
constructor(parcel: Parcel) : this() {
|
||||
value = parcel.readInt()
|
||||
key = parcel.readString()
|
||||
type = parcel.readString()
|
||||
dialog = parcel.readString()
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
parcel.writeInt(value)
|
||||
parcel.writeString(key)
|
||||
parcel.writeString(type)
|
||||
parcel.writeString(dialog)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
return 0
|
||||
}
|
||||
|
||||
companion object CREATOR : Parcelable.Creator<TaskDirectionDataDrop> {
|
||||
override fun createFromParcel(parcel: Parcel): TaskDirectionDataDrop {
|
||||
return TaskDirectionDataDrop(parcel)
|
||||
}
|
||||
|
||||
override fun newArray(size: Int): Array<TaskDirectionDataDrop?> {
|
||||
return arrayOfNulls(size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,10 +8,10 @@ class TaskScoringResult(): Parcelable {
|
|||
constructor(data: TaskDirectionData, stats: AvatarStats?) : this() {
|
||||
hasLeveledUp = data.lvl > (stats?.lvl ?: 0)
|
||||
healthDelta = data.hp - (stats?.hp ?: 0.0)
|
||||
if (hasLeveledUp) {
|
||||
experienceDelta = (stats?.toNextLevel ?: 0).toDouble() - (stats?.exp ?: 0.0) + data.exp
|
||||
experienceDelta = if (hasLeveledUp) {
|
||||
(stats?.toNextLevel ?: 0).toDouble() - (stats?.exp ?: 0.0) + data.exp
|
||||
} else {
|
||||
experienceDelta = data.exp - (stats?.exp ?: 0.0)
|
||||
data.exp - (stats?.exp ?: 0.0)
|
||||
}
|
||||
manaDelta = data.mp - (stats?.mp ?: 0.0)
|
||||
goldDelta = data.gp - (stats?.gp ?: 0.0)
|
||||
|
|
@ -40,6 +40,7 @@ class TaskScoringResult(): Parcelable {
|
|||
level = parcel.readValue(Int::class.java.classLoader) as? Int
|
||||
questDamage = parcel.readValue(Double::class.java.classLoader) as? Double
|
||||
questItemsFound = parcel.readValue(Int::class.java.classLoader) as? Int
|
||||
drop = parcel.readValue(TaskDirectionDataDrop::class.java.classLoader) as? TaskDirectionDataDrop
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
|
|
@ -51,6 +52,7 @@ class TaskScoringResult(): Parcelable {
|
|||
parcel.writeValue(level)
|
||||
parcel.writeValue(questDamage)
|
||||
parcel.writeValue(questItemsFound)
|
||||
parcel.writeValue(drop)
|
||||
}
|
||||
|
||||
override fun describeContents(): Int {
|
||||
|
|
|
|||
|
|
@ -13,7 +13,9 @@ import androidx.core.content.ContextCompat
|
|||
import com.habitrpg.common.habitica.R
|
||||
import com.habitrpg.common.habitica.models.PlayerTier
|
||||
|
||||
class UsernameLabel(context: Context?, attrs: AttributeSet?) : LinearLayout(context, attrs) {
|
||||
class UsernameLabel @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : LinearLayout(context, attrs) {
|
||||
|
||||
private val textView = TextView(context)
|
||||
private val tierIconView = ImageView(context)
|
||||
|
|
@ -54,7 +56,7 @@ class UsernameLabel(context: Context?, attrs: AttributeSet?) : LinearLayout(cont
|
|||
textViewParams.gravity = Gravity.CENTER_VERTICAL
|
||||
textViewParams.weight = 1.0f
|
||||
addView(textView, textViewParams)
|
||||
val padding = context?.resources?.getDimension(R.dimen.spacing_small)?.toInt() ?: 0
|
||||
val padding = context.resources.getDimension(R.dimen.spacing_small).toInt()
|
||||
textView.setPadding(0, 0, padding, 0)
|
||||
textView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 14f)
|
||||
val iconViewParams = LayoutParams(
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import javax.inject.Inject
|
||||
|
||||
class TaskRepository @Inject constructor(val apiClient: ApiClient, val localRepository: TaskLocalRepository) {
|
||||
class TaskRepository @Inject constructor(val apiClient: ApiClient, val localRepository: TaskLocalRepository, val userLocalRepository: UserLocalRepository) {
|
||||
|
||||
suspend fun retrieveTasks(): TaskList? {
|
||||
val tasks = apiClient.getTasks()
|
||||
|
|
@ -25,9 +25,36 @@ class TaskRepository @Inject constructor(val apiClient: ApiClient, val localRepo
|
|||
val result = apiClient.scoreTask(id, direction.text)
|
||||
if (result != null) {
|
||||
task.completed = direction == TaskDirection.UP
|
||||
task.value += result.delta
|
||||
if (task.type == TaskType.HABIT) {
|
||||
if (direction == TaskDirection.UP) {
|
||||
task.counterUp = task.counterUp?.plus(1) ?: 1
|
||||
} else {
|
||||
task.counterUp = task.counterDown?.plus(1) ?: 1
|
||||
}
|
||||
} else if (task.type == TaskType.DAILY) {
|
||||
if (direction == TaskDirection.UP) {
|
||||
task.streak = task.streak?.plus(1) ?: 1
|
||||
} else {
|
||||
task.streak = task.streak?.minus(1) ?: 0
|
||||
}
|
||||
}
|
||||
localRepository.updateTask(task)
|
||||
}
|
||||
return result?.let { TaskScoringResult(it, user?.stats) }
|
||||
val scoringResult = result?.let { TaskScoringResult(it, user?.stats) }
|
||||
if (user != null) {
|
||||
user.stats?.hp = result?.hp
|
||||
user.stats?.exp = result?.exp
|
||||
user.stats?.mp = result?.mp
|
||||
user.stats?.gp = result?.gp
|
||||
user.stats?.lvl = result?.lvl
|
||||
/*user?.party?.quest?.progress?.up = (
|
||||
user?.party?.quest?.progress?.up
|
||||
?: 0F
|
||||
) + (result?._tmp?.quest?.progressDelta?.toFloat() ?: 0F)*/
|
||||
userLocalRepository.saveUser(user)
|
||||
}
|
||||
return scoringResult
|
||||
}
|
||||
|
||||
fun getTask(taskID: String?): Flow<Task?> {
|
||||
|
|
|
|||
|
|
@ -2,14 +2,16 @@ package com.habitrpg.wearos.habitica.ui.activities
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityTaskDetailBinding
|
||||
import com.habitrpg.wearos.habitica.ui.viewmodels.TaskDetailViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import java.util.Locale
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TaskDetailActivity: BaseActivity<ActivityTaskDetailBinding, TaskDetailViewModel>() {
|
||||
class TaskDetailActivity : BaseActivity<ActivityTaskDetailBinding, TaskDetailViewModel>() {
|
||||
|
||||
override val viewModel: TaskDetailViewModel by viewModels()
|
||||
|
||||
|
|
@ -29,15 +31,21 @@ class TaskDetailActivity: BaseActivity<ActivityTaskDetailBinding, TaskDetailView
|
|||
}
|
||||
|
||||
private fun subscribeUI() {
|
||||
viewModel.task.observe(this) {
|
||||
binding.taskTypeView.text = it?.type?.value?.replaceFirstChar {
|
||||
viewModel.task.observe(this) { task ->
|
||||
binding.taskTypeView.text = task?.type?.value?.replaceFirstChar {
|
||||
if (it.isLowerCase()) it.titlecase(
|
||||
Locale.getDefault()
|
||||
) else it.toString()
|
||||
}
|
||||
binding.taskTextView.text = it?.text
|
||||
if (it?.notes?.isNotBlank() == true) {
|
||||
binding.taskNotesView.text = it.notes
|
||||
binding.taskTypeView.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
this,
|
||||
task?.extraLightTaskColor ?: R.color.white
|
||||
)
|
||||
)
|
||||
binding.taskTextView.text = task?.text
|
||||
if (task?.notes?.isNotBlank() == true) {
|
||||
binding.taskNotesView.text = task.notes
|
||||
binding.taskNotesView.isVisible = true
|
||||
} else {
|
||||
binding.taskNotesView.isVisible = false
|
||||
|
|
|
|||
|
|
@ -28,8 +28,15 @@ class TaskFormActivity : BaseActivity<ActivityTaskFormBinding, TaskFormViewModel
|
|||
updateTaskTypeButton(binding.todoButton, TaskType.TODO)
|
||||
updateTaskTypeButton(binding.dailyButton, TaskType.DAILY)
|
||||
updateTaskTypeButton(binding.habitButton, TaskType.HABIT)
|
||||
binding.confirmationTitle.text = getString(R.string.new_task_x, taskType?.value)
|
||||
binding.saveButton.text = getString(R.string.save_task_x, taskType?.value)
|
||||
val typeName = getString(when(value) {
|
||||
TaskType.HABIT -> R.string.habit
|
||||
TaskType.DAILY -> R.string.daily
|
||||
TaskType.TODO -> R.string.todo
|
||||
TaskType.REWARD -> R.string.reward
|
||||
else -> R.string.task
|
||||
})
|
||||
binding.confirmationTitle.text = getString(R.string.new_task_x, typeName)
|
||||
binding.saveButton.text = getString(R.string.save_task_x, typeName)
|
||||
}
|
||||
override val viewModel: TaskFormViewModel by viewModels()
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.preference.PreferenceManager
|
|||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityTaskResultBinding
|
||||
import com.habitrpg.android.habitica.databinding.TaskRewardDropBinding
|
||||
import com.habitrpg.android.habitica.extensions.localizedCapitalize
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.common.habitica.models.responses.TaskScoringResult
|
||||
|
|
@ -155,14 +156,16 @@ class TaskResultActivity : BaseActivity<ActivityTaskResultBinding, TaskResultVie
|
|||
}
|
||||
}
|
||||
if (viewModel.result?.drop?.key != null) {
|
||||
elements.add(getString(R.string.some_x, viewModel.result?.drop?.type))
|
||||
dropBinding.imageView.loadImage(viewModel.result?.drop?.key)
|
||||
val type = viewModel.result?.drop?.type
|
||||
val key = viewModel.result?.drop?.key
|
||||
elements.add(getString(R.string.some_x, type))
|
||||
dropBinding.imageView.loadImage("Pet_" + type + "_" + key)
|
||||
}
|
||||
dropBinding.textView.text = when (elements.size) {
|
||||
1 -> elements[0]
|
||||
2 -> getString(R.string.x_and_y, elements[0], elements[1])
|
||||
else -> elements.joinToString(", ")
|
||||
}
|
||||
}.localizedCapitalize()
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,9 +2,9 @@ package com.habitrpg.wearos.habitica.ui.adapters
|
|||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.android.habitica.databinding.RowHeaderBinding
|
||||
import com.habitrpg.android.habitica.databinding.RowHubBinding
|
||||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.wearos.habitica.models.user.MenuItem
|
||||
import com.habitrpg.wearos.habitica.ui.viewHolders.HeaderViewHolder
|
||||
import com.habitrpg.wearos.habitica.ui.viewHolders.HubViewHolder
|
||||
|
|
@ -27,19 +27,14 @@ class HubAdapter: RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
|||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is HubViewHolder) {
|
||||
val item = data[position - 1]
|
||||
holder.bind(item)
|
||||
holder.bind(getItemAt(position - 1))
|
||||
} else if (holder is HeaderViewHolder){
|
||||
holder.bind(title)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) 0 else 1
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return data.size + 1
|
||||
}
|
||||
private fun getItemAt(position: Int) = data.filter { !it.isHidden }[position]
|
||||
override fun getItemViewType(position: Int) = if (position == 0) 0 else 1
|
||||
override fun getItemCount() = data.filter { !it.isHidden }.size + 1
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,28 +3,15 @@ package com.habitrpg.wearos.habitica.ui.viewHolders.tasks
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import com.habitrpg.android.habitica.databinding.RowDailyBinding
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
|
||||
class DailyViewHolder(itemView: View) : CheckedTaskViewHolder(itemView) {
|
||||
private val binding = RowDailyBinding.bind(itemView)
|
||||
override val titleView: TextView
|
||||
override val titleView: TaskTextView
|
||||
get() = binding.title
|
||||
override val checkbox: ImageView
|
||||
get() = binding.checkbox
|
||||
override val checkboxWrapper: ViewGroup
|
||||
get() = binding.checkboxWrapper
|
||||
|
||||
override fun bind(data: Task) {
|
||||
super.bind(data)
|
||||
val streakString = data.streakString
|
||||
if (streakString?.isNotBlank() == true) {
|
||||
binding.streakView.text = streakString
|
||||
binding.streakView.isVisible = true
|
||||
} else {
|
||||
binding.streakView.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -2,16 +2,15 @@ package com.habitrpg.wearos.habitica.ui.viewHolders.tasks
|
|||
|
||||
import android.content.res.ColorStateList
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.RowHabitBinding
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
|
||||
class HabitViewHolder(itemView: View) : TaskViewHolder(itemView) {
|
||||
private val binding = RowHabitBinding.bind(itemView)
|
||||
override val titleView: TextView
|
||||
override val titleView: TaskTextView
|
||||
get() = binding.title
|
||||
|
||||
init {
|
||||
|
|
@ -43,12 +42,5 @@ class HabitViewHolder(itemView: View) : TaskViewHolder(itemView) {
|
|||
binding.habitButtonIcon.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, data.mediumTaskColor))
|
||||
binding.habitButtonIcon.imageTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.white))
|
||||
}
|
||||
val streakString = data.streakString
|
||||
if (streakString?.isNotBlank() == true) {
|
||||
binding.streakView.text = streakString
|
||||
binding.streakView.isVisible = true
|
||||
} else {
|
||||
binding.streakView.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -4,10 +4,11 @@ import android.view.View
|
|||
import android.widget.TextView
|
||||
import com.habitrpg.android.habitica.databinding.RowRewardBinding
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
|
||||
class RewardViewHolder(itemView: View) : TaskViewHolder(itemView) {
|
||||
private val binding = RowRewardBinding.bind(itemView)
|
||||
override val titleView: TextView
|
||||
override val titleView: TaskTextView
|
||||
get() = binding.title
|
||||
|
||||
override fun bind(data: Task) {
|
||||
|
|
|
|||
|
|
@ -1,13 +1,13 @@
|
|||
package com.habitrpg.wearos.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.ui.viewHolders.BindableViewHolder
|
||||
import com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
|
||||
abstract class TaskViewHolder(itemView: View) : BindableViewHolder<Task>(itemView) {
|
||||
var onTaskScore: (() -> Unit)? = null
|
||||
abstract val titleView: TextView
|
||||
abstract val titleView: TaskTextView
|
||||
override fun bind(data: Task) {
|
||||
titleView.text = data.text
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,10 +5,11 @@ import android.view.ViewGroup
|
|||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import com.habitrpg.android.habitica.databinding.RowTodoBinding
|
||||
import com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
|
||||
class ToDoViewHolder(itemView: View) : CheckedTaskViewHolder(itemView) {
|
||||
private val binding = RowTodoBinding.bind(itemView)
|
||||
override val titleView: TextView
|
||||
override val titleView: TaskTextView
|
||||
get() = binding.title
|
||||
override val checkbox: ImageView
|
||||
get() = binding.checkbox
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ class HabiticaScrollView @JvmOverloads constructor(
|
|||
horizontalPadding,
|
||||
verticalPadding,
|
||||
horizontalPadding,
|
||||
verticalPadding
|
||||
verticalPadding*2
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,74 @@
|
|||
package com.habitrpg.wearos.habitica.ui.views
|
||||
|
||||
import android.content.Context
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.widget.AppCompatTextView
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
||||
/**
|
||||
* A custom [AppCompatTextView] that allows custom ellipsis and ellipsisColor.
|
||||
*/
|
||||
open class EllipsizedTextView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) : AppCompatTextView(context, attrs, defStyleAttr) {
|
||||
var ellipsis = getDefaultEllipsis().toString()
|
||||
set(value) {
|
||||
field = value
|
||||
ellipsisSpannable = SpannableString(ellipsis)
|
||||
}
|
||||
var ellipsisColor = getDefaultEllipsisColor()
|
||||
|
||||
private var ellipsisSpannable: SpannableString
|
||||
private val spannableStringBuilder = SpannableStringBuilder()
|
||||
|
||||
init {
|
||||
if (attrs != null) {
|
||||
val typedArray = context.theme.obtainStyledAttributes(attrs, R.styleable.EllipsizedTextView, 0, 0)
|
||||
typedArray.let {
|
||||
ellipsis = typedArray.getString(R.styleable.EllipsizedTextView_ellipsis) ?: getDefaultEllipsis().toString()
|
||||
ellipsisColor = typedArray.getColor(R.styleable.EllipsizedTextView_ellipsisColor, getDefaultEllipsisColor())
|
||||
typedArray.recycle()
|
||||
}
|
||||
}
|
||||
|
||||
ellipsisSpannable = SpannableString(ellipsis)
|
||||
ellipsisSpannable.setSpan(ForegroundColorSpan(ellipsisColor), 0, ellipsis.length, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
}
|
||||
|
||||
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
|
||||
super.onMeasure(widthMeasureSpec, heightMeasureSpec)
|
||||
val availableScreenWidth = measuredWidth - compoundPaddingLeft.toFloat() - compoundPaddingRight.toFloat()
|
||||
var availableTextWidth = availableScreenWidth * maxLines
|
||||
var ellipsizedText = TextUtils.ellipsize(text, paint, availableTextWidth, ellipsize)
|
||||
|
||||
if (ellipsizedText.toString() != text.toString()) {
|
||||
// If the ellipsizedText is different than the original text, this means that it didn't fit and got indeed ellipsized.
|
||||
// Calculate the new availableTextWidth by taking into consideration the size of the custom ellipsis, too.
|
||||
availableTextWidth = (availableScreenWidth - paint.measureText(ellipsis)) * maxLines
|
||||
ellipsizedText = TextUtils.ellipsize(text, paint, availableTextWidth, ellipsize)
|
||||
val defaultEllipsisStart = ellipsizedText.indexOf(getDefaultEllipsis())
|
||||
val defaultEllipsisEnd = defaultEllipsisStart + 1
|
||||
|
||||
spannableStringBuilder.clear()
|
||||
|
||||
// Update the text with the ellipsized version and replace the default ellipsis with the custom one.
|
||||
text = spannableStringBuilder.append(ellipsizedText).replace(defaultEllipsisStart, defaultEllipsisEnd, ellipsisSpannable)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getDefaultEllipsis(): Char {
|
||||
return Typography.ellipsis
|
||||
}
|
||||
|
||||
private fun getDefaultEllipsisColor(): Int {
|
||||
return textColors.defaultColor
|
||||
}
|
||||
}
|
||||
|
||||
class TaskTextView @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : EllipsizedTextView(context, attrs) {
|
||||
}
|
||||
|
|
@ -26,20 +26,15 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
<com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
tools:text="Task Title"
|
||||
style="@style/Text.Body2"/>
|
||||
<TextView
|
||||
android:id="@+id/streak_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:drawableStartCompat="@drawable/task_icon_streak"
|
||||
android:drawablePadding="@dimen/spacing_small"
|
||||
android:visibility="gone"/>
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Task Title that is long and wraps and"
|
||||
android:textColor="@color/white"
|
||||
style="@style/Text.Body1"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
@ -25,20 +25,15 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
<com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Task Title"
|
||||
android:textColor="@color/white"
|
||||
style="@style/Text.Body2"/>
|
||||
<TextView
|
||||
android:id="@+id/streak_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:drawableStartCompat="@drawable/task_icon_streak"
|
||||
android:drawablePadding="@dimen/spacing_small"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
@ -7,12 +7,14 @@
|
|||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/Chip">
|
||||
<TextView
|
||||
<com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Task Title"
|
||||
android:textColor="@color/white"
|
||||
style="@style/Text.Body2"/>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
@ -27,20 +27,15 @@
|
|||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
<TextView
|
||||
<com.habitrpg.wearos.habitica.ui.views.TaskTextView
|
||||
android:id="@+id/title"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lines="1"
|
||||
android:maxLines="2"
|
||||
android:ellipsize="end"
|
||||
tools:text="Task Title"
|
||||
android:textColor="@color/white"
|
||||
style="@style/Text.Body2"/>
|
||||
<TextView
|
||||
android:id="@+id/streak_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
app:drawableStartCompat="@drawable/task_icon_streak"
|
||||
android:drawablePadding="@dimen/spacing_small"
|
||||
android:visibility="gone"/>
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
</FrameLayout>
|
||||
|
|
@ -16,6 +16,7 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:fontFamily="sans-serif-medium"
|
||||
android:textSize="14sp"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/white"
|
||||
/>
|
||||
<com.habitrpg.common.habitica.views.PixelArtView
|
||||
|
|
|
|||
|
|
@ -9,4 +9,8 @@
|
|||
<attr name="backgroundArcColor" format="color" />
|
||||
<attr name="arcFillColor" format="color" />
|
||||
</declare-styleable>
|
||||
<declare-styleable name="EllipsizedTextView">
|
||||
<attr name="ellipsis" format="string" />
|
||||
<attr name="ellipsisColor" format="color" />
|
||||
</declare-styleable>
|
||||
</resources>
|
||||
|
|
@ -15,6 +15,6 @@
|
|||
<dimen name="row_text_size">16sp</dimen>
|
||||
<dimen name="row_padding_horizontal">10dp</dimen>
|
||||
<dimen name="row_padding_vertical">9dp</dimen>
|
||||
<dimen name="row_side_spacing">11dp</dimen>
|
||||
<dimen name="row_side_spacing">10dp</dimen>
|
||||
<dimen name="row_spacing">2dp</dimen>
|
||||
</resources>
|
||||
Loading…
Reference in a new issue