Merge branch 'main' into hafiz/settings-updates
50
.github/workflows/android.yml
vendored
|
|
@ -44,31 +44,31 @@ jobs:
|
|||
with:
|
||||
arguments: testProdDebugUnitTest
|
||||
|
||||
ui-test:
|
||||
runs-on: macos-latest
|
||||
strategy:
|
||||
matrix:
|
||||
api-level: [24, 26, 28, 29, 30, 31]
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: set up JDK 11
|
||||
uses: actions/setup-java@v2
|
||||
with:
|
||||
java-version: '11'
|
||||
distribution: 'adopt'
|
||||
cache: gradle
|
||||
- name: Prepare
|
||||
run: ./.github/prepare-workflow
|
||||
- name: Validate Gradle wrapper
|
||||
uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
|
||||
- name: run tests
|
||||
uses: reactivecircus/android-emulator-runner@v2
|
||||
with:
|
||||
api-level: ${{ matrix.api-level }}
|
||||
arch: x86_64
|
||||
emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
disable-animations: true
|
||||
script: ./gradlew connectedProdDebugAndroidTest
|
||||
# ui-test:
|
||||
# runs-on: macos-latest
|
||||
# strategy:
|
||||
# matrix:
|
||||
# api-level: [24, 26, 28, 29, 30, 31]
|
||||
# steps:
|
||||
# - uses: actions/checkout@v2
|
||||
# - name: set up JDK 11
|
||||
# uses: actions/setup-java@v2
|
||||
# with:
|
||||
# java-version: '11'
|
||||
# distribution: 'adopt'
|
||||
# cache: gradle
|
||||
# - name: Prepare
|
||||
# run: ./.github/prepare-workflow
|
||||
# - name: Validate Gradle wrapper
|
||||
# uses: gradle/wrapper-validation-action@e6e38bacfdf1a337459f332974bb2327a31aaf4b
|
||||
# - name: run tests
|
||||
# uses: reactivecircus/android-emulator-runner@v2
|
||||
# with:
|
||||
# api-level: ${{ matrix.api-level }}
|
||||
# arch: x86_64
|
||||
# emulator-options: -no-window -gpu swiftshader_indirect -noaudio -no-boot-anim -camera-back none
|
||||
# disable-animations: true
|
||||
# script: ./gradlew connectedProdDebugAndroidTest
|
||||
|
||||
lint:
|
||||
runs-on: ubuntu-latest
|
||||
|
|
|
|||
|
|
@ -150,7 +150,7 @@ android {
|
|||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
resConfigs 'en', 'bg', 'de', 'en-rGB', 'es', 'fr', 'hr-rHR', 'in', 'it', 'iw', 'ja', 'ko', 'lt', 'nl', 'pl', 'pt-rBR', 'pt-rPT', 'ru', 'tr', 'zh', 'zh-rTW'
|
||||
|
||||
versionCode 4000
|
||||
versionCode app_version_code
|
||||
versionName app_version_name
|
||||
|
||||
targetSdkVersion target_sdk
|
||||
|
|
@ -212,22 +212,26 @@ android {
|
|||
dimension "buildType"
|
||||
buildConfigField "String", "TESTING_LEVEL", "\"staff\""
|
||||
resValue "string", "app_name", "Habitica Staff"
|
||||
versionCode app_version_code + 6
|
||||
}
|
||||
|
||||
alpha {
|
||||
dimension "buildType"
|
||||
buildConfigField "String", "TESTING_LEVEL", "\"alpha\""
|
||||
resValue "string", "app_name", "Habitica Alpha"
|
||||
versionCode app_version_code + 4
|
||||
}
|
||||
|
||||
beta {
|
||||
buildConfigField "String", "TESTING_LEVEL", "\"beta\""
|
||||
dimension "buildType"
|
||||
versionCode app_version_code + 2
|
||||
}
|
||||
|
||||
prod {
|
||||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
dimension "buildType"
|
||||
versionCode app_version_code
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
Before Width: | Height: | Size: 746 B |
|
Before Width: | Height: | Size: 499 B |
|
Before Width: | Height: | Size: 954 B |
|
Before Width: | Height: | Size: 1.7 KiB |
|
Before Width: | Height: | Size: 2.6 KiB |
|
|
@ -4,8 +4,7 @@
|
|||
<dimen name="activity_horizontal_margin">16dp</dimen>
|
||||
<dimen name="activity_vertical_margin">8dp</dimen>
|
||||
|
||||
<dimen name="card_medium_text">18.0sp</dimen>
|
||||
<dimen name="card_small_text">14.0sp</dimen>
|
||||
|
||||
<dimen name="card_padding">12dp</dimen>
|
||||
<dimen name="card_padding_compact">6dp</dimen>
|
||||
<dimen name="section_leftright_padding">6dp</dimen>
|
||||
|
|
|
|||
|
|
@ -270,7 +270,6 @@
|
|||
• Bosses won’t do damage for your missed Dailies\n
|
||||
• Your boss damage or collection quest items will stay pending until check-out</string>
|
||||
<string name="empty_items">You don\'t have any %s</string>
|
||||
<string name="user_level">Lvl %d</string>
|
||||
<string name="user_level_with_class">Lvl %1$d %2$s</string>
|
||||
<string name="user_level_with_class_unabbreviated">Level %1$d %2$s</string>
|
||||
<string name="warrior">Warrior</string>
|
||||
|
|
@ -1138,9 +1137,7 @@
|
|||
<string name="locked_equipment_shop_dialog">You must purchase the previous items in this sequence to unlock</string>
|
||||
<string name="caused_damage">You caused damage to the boss</string>
|
||||
<string name="avatar_style">Style</string>
|
||||
<string name="failed">Failed to load</string>
|
||||
<string name="daily_item_checkbox">Daily Checkbox</string>
|
||||
<string name="retry">Retry</string>
|
||||
<string name="empty_title">Nothing here yet</string>
|
||||
<string name="empty_equipment_description">You don\'t have any equipment of this type yet. You can purchase equipment from the market using the gold you earned.</string>
|
||||
<string name="todo_item_checkbox">Todo Checkbox</string>
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class ChallengeRepositoryImpl(
|
|||
|
||||
for ((key, value) in stringListMap) {
|
||||
val taskIdList = value.map { t -> t.id ?: "" }
|
||||
|
||||
if (key == null) continue
|
||||
when (key) {
|
||||
TaskType.HABIT -> tasksOrder.habits = taskIdList
|
||||
TaskType.DAILY -> tasksOrder.dailys = taskIdList
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -1,11 +1,11 @@
|
|||
package com.habitrpg.android.habitica.interactors
|
||||
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirectionData
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirectionData
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import kotlin.math.min
|
||||
import kotlin.math.pow
|
||||
import kotlin.math.roundToLong
|
||||
|
|
@ -46,6 +46,9 @@ class ScoreTaskLocallyInteractor {
|
|||
private fun scoreToDo(user: User, task: Task, direction: TaskDirection) {
|
||||
}
|
||||
|
||||
private fun scoreReward(user: User, task: Task, direction: TaskDirection) {
|
||||
}
|
||||
|
||||
fun score(user: User, task: Task, direction: TaskDirection): TaskDirectionData? {
|
||||
return if (task.type == TaskType.HABIT || direction == TaskDirection.UP) {
|
||||
val stats = user.stats ?: return null
|
||||
|
|
@ -67,14 +70,16 @@ class ScoreTaskLocallyInteractor {
|
|||
TaskType.HABIT -> scoreHabit(user, task, direction)
|
||||
TaskType.DAILY -> scoreDaily(user, task, direction)
|
||||
TaskType.TODO -> scoreToDo(user, task, direction)
|
||||
TaskType.REWARD -> scoreReward(user, task, direction)
|
||||
else -> {}
|
||||
}
|
||||
|
||||
if (result.hp <= 0.0) {
|
||||
result.hp = 0.0
|
||||
}
|
||||
if (result.exp >= stats.toNextLevel?.toDouble() ?: 0.0) {
|
||||
if (result.exp >= (stats.toNextLevel?.toDouble() ?: 0.0)) {
|
||||
result.exp = result.exp - (stats.toNextLevel?.toDouble() ?: 0.0)
|
||||
result.lvl = user.stats?.lvl ?: 0 + 1
|
||||
result.lvl = (user.stats?.lvl ?: 0) + 1
|
||||
result.hp = 50.0
|
||||
} else {
|
||||
result.lvl = user.stats?.lvl ?: 0
|
||||
|
|
|
|||
|
|
@ -29,7 +29,6 @@ import java.util.Date
|
|||
import java.util.GregorianCalendar
|
||||
|
||||
open class Task : RealmObject, BaseMainObject, Parcelable {
|
||||
|
||||
override val realmClass: Class<Task>
|
||||
get() = Task::class.java
|
||||
override val primaryIdentifier: String?
|
||||
|
|
@ -109,6 +108,21 @@ open class Task : RealmObject, BaseMainObject, Parcelable {
|
|||
val completedChecklistCount: Int
|
||||
get() = checklist?.count { it.completed } ?: 0
|
||||
|
||||
val streakString: String?
|
||||
get() {
|
||||
return if (counterUp != null && (counterUp ?: 0) > 0 && counterDown != null && (counterDown ?: 0) > 0) {
|
||||
"+" + counterUp.toString() + " | -" + counterDown?.toString()
|
||||
} else if (counterUp != null && (counterUp ?: 0) > 0) {
|
||||
"+" + counterUp.toString()
|
||||
} else if (counterDown != null && (counterDown ?: 0) > 0) {
|
||||
"-" + counterDown.toString()
|
||||
} else if ((streak ?: 0) > 0) {
|
||||
return streak.toString()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val extraLightTaskColor: Int
|
||||
get() {
|
||||
return when {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import com.habitrpg.android.habitica.models.user.Outfit
|
|||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel
|
||||
import com.habitrpg.android.habitica.ui.adapter.social.AchievementProfileAdapter
|
||||
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewState
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewState
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
|
|
|
|||
|
|
@ -496,7 +496,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
|
||||
private fun checkMaintenance() {
|
||||
viewModel.ifNeedsMaintenance { maintenanceResponse ->
|
||||
if (maintenanceResponse.activeMaintenance) {
|
||||
if (maintenanceResponse.activeMaintenance == true) {
|
||||
val intent = createMaintenanceIntent(maintenanceResponse, false)
|
||||
startActivity(intent)
|
||||
} else {
|
||||
|
|
@ -504,7 +504,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
try {
|
||||
val packageInfo = packageManager.getPackageInfo(packageName, 0)
|
||||
@Suppress("DEPRECATION")
|
||||
if (packageInfo.versionCode < maintenanceResponse.minBuild) {
|
||||
if (packageInfo.versionCode < (maintenanceResponse.minBuild ?: 0)) {
|
||||
val intent = createMaintenanceIntent(maintenanceResponse, true)
|
||||
startActivity(intent)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -72,7 +72,7 @@ class MaintenanceActivity : BaseActivity() {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ maintenanceResponse ->
|
||||
if (!maintenanceResponse.activeMaintenance) {
|
||||
if (maintenanceResponse.activeMaintenance == false) {
|
||||
finish()
|
||||
}
|
||||
},
|
||||
|
|
|
|||
|
|
@ -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)))
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,7 +15,7 @@ import com.habitrpg.android.habitica.helpers.MainNavigationController
|
|||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.EquipmentRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import com.habitrpg.android.habitica.models.user.User
|
|||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseDialogFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ import com.habitrpg.android.habitica.ui.activities.MainActivity
|
|||
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ import com.habitrpg.android.habitica.models.social.Group
|
|||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.ShopRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.RecyclerViewState
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewState
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CurrencyViews
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import com.habitrpg.android.habitica.models.inventory.Egg
|
|||
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.StableRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.MarginDecoration
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
|
|
|
|||
|
|
@ -24,21 +24,21 @@ import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
|||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import com.habitrpg.android.habitica.ui.activities.ChallengeFormActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.FullProfileActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.common.habitica.helpers.EmojiParser
|
||||
import com.habitrpg.common.habitica.helpers.setMarkdown
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.DailyViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.HabitViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.TodoViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.common.habitica.views.HabiticaIconsHelper
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import javax.inject.Inject
|
||||
import com.habitrpg.common.habitica.helpers.EmojiParser
|
||||
import com.habitrpg.common.habitica.helpers.setMarkdown
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import com.habitrpg.common.habitica.views.HabiticaIconsHelper
|
||||
import retrofit2.HttpException
|
||||
import javax.inject.Inject
|
||||
|
||||
class ChallengeDetailFragment : BaseMainFragment<FragmentChallengeDetailBinding>() {
|
||||
|
||||
|
|
@ -117,7 +117,8 @@ class ChallengeDetailFragment : BaseMainFragment<FragmentChallengeDetailBinding>
|
|||
val rewards = ArrayList<Task>()
|
||||
|
||||
for (entry in taskList) {
|
||||
when (entry.type) {
|
||||
val type = entry.type ?: continue
|
||||
when (type) {
|
||||
TaskType.TODO -> todos.add(entry)
|
||||
TaskType.HABIT -> habits.add(entry)
|
||||
TaskType.DAILY -> dailies.add(entry)
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ import com.habitrpg.android.habitica.models.social.Group
|
|||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.adapter.social.ChallengesListViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
import io.reactivex.rxjava3.kotlin.Flowables
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBind
|
|||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.ui.adapter.social.GuildListAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.KeyboardUtil
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import javax.inject.Inject
|
||||
|
|
|
|||
|
|
@ -28,10 +28,7 @@ import com.habitrpg.android.habitica.helpers.MainNavigationController
|
|||
import com.habitrpg.android.habitica.helpers.NotificationsManager
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.SoundManager
|
||||
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.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
|
||||
|
|
@ -41,13 +38,16 @@ import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter
|
|||
import com.habitrpg.android.habitica.ui.adapter.tasks.TaskRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.adapter.tasks.TodosRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.BaseTaskViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
import com.habitrpg.common.habitica.views.HabiticaIconsHelper
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.common.habitica.models.responses.TaskScoringResult
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import com.habitrpg.common.habitica.views.HabiticaIconsHelper
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import kotlinx.coroutines.Job
|
||||
|
|
@ -471,6 +471,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
viewModel?.setActiveFilter(TaskType.DAILY, Task.FILTER_ACTIVE)
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -534,6 +535,10 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
fragment.tutorialStepIdentifier = "todos"
|
||||
tutorialTexts = listOf(context.getString(R.string.tutorial_todos_1), context.getString(R.string.tutorial_todos_2))
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
fragment.tutorialStepIdentifier = "rewards"
|
||||
tutorialTexts = listOf(context.getString(R.string.tutorial_rewards_1), context.getString(R.string.tutorial_rewards_2))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,34 +2,18 @@ package com.habitrpg.android.habitica.ui.helpers
|
|||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.EmptyItemBinding
|
||||
import com.habitrpg.android.habitica.databinding.FailedItemBinding
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewState
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewStateAdapter
|
||||
|
||||
data class EmptyItem(
|
||||
var title: String,
|
||||
var text: String? = null,
|
||||
var iconResource: Int? = null,
|
||||
var buttonLabel: String? = null,
|
||||
var onButtonTap: (() -> Unit)? = null
|
||||
)
|
||||
class RecyclerViewEmptySupport @JvmOverloads constructor(
|
||||
context: Context, attrs: AttributeSet? = null
|
||||
) : RecyclerView(context, attrs) {
|
||||
var onRefresh: (() -> Unit)?
|
||||
get() = emptyAdapter.onRefresh
|
||||
set(value) { emptyAdapter.onRefresh = value }
|
||||
|
||||
enum class RecyclerViewState {
|
||||
LOADING,
|
||||
EMPTY,
|
||||
DISPLAYING_DATA,
|
||||
FAILED
|
||||
}
|
||||
|
||||
class RecyclerViewEmptySupport : RecyclerView {
|
||||
var onRefresh: (() -> Unit)? = null
|
||||
var state: RecyclerViewState = RecyclerViewState.LOADING
|
||||
set(value) {
|
||||
field = value
|
||||
|
|
@ -37,7 +21,7 @@ class RecyclerViewEmptySupport : RecyclerView {
|
|||
RecyclerViewState.DISPLAYING_DATA -> updateAdapter(actualAdapter)
|
||||
else -> {
|
||||
updateAdapter(emptyAdapter)
|
||||
emptyAdapter.notifyDataSetChanged()
|
||||
emptyAdapter.state = value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -48,53 +32,16 @@ class RecyclerViewEmptySupport : RecyclerView {
|
|||
}
|
||||
}
|
||||
|
||||
var emptyItem: EmptyItem? = null
|
||||
var emptyItem: EmptyItem?
|
||||
get() = emptyAdapter.emptyItem
|
||||
set(value) {
|
||||
field = value
|
||||
emptyAdapter.notifyDataSetChanged()
|
||||
emptyAdapter.emptyItem = value
|
||||
}
|
||||
|
||||
private var actualAdapter: Adapter<*>? = null
|
||||
private val emptyAdapter: Adapter<ViewHolder> = object : Adapter<ViewHolder>() {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
|
||||
return when (viewType) {
|
||||
0 -> {
|
||||
val view = parent.inflate(R.layout.loading_item)
|
||||
val animation1 = AlphaAnimation(0.0f, 1.0f)
|
||||
animation1.duration = 300
|
||||
animation1.startOffset = 500
|
||||
animation1.fillAfter = true
|
||||
view.findViewById<ProgressBar>(R.id.loading_indicator).startAnimation(animation1)
|
||||
object : ViewHolder(view) {}
|
||||
}
|
||||
1 -> FailedViewHolder(parent.inflate(R.layout.failed_item))
|
||||
else -> EmptyViewHolder(parent.inflate(R.layout.empty_item))
|
||||
}
|
||||
}
|
||||
private val emptyAdapter = RecyclerViewStateAdapter()
|
||||
|
||||
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
|
||||
if (holder is EmptyViewHolder) {
|
||||
holder.bind(emptyItem)
|
||||
} else if (holder is FailedViewHolder) {
|
||||
holder.bind(onRefresh)
|
||||
}
|
||||
(holder as? EmptyViewHolder)?.bind(emptyItem)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when (state) {
|
||||
RecyclerViewState.LOADING -> 0
|
||||
RecyclerViewState.FAILED -> 1
|
||||
else -> 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val observer = object : RecyclerView.AdapterDataObserver() {
|
||||
private val observer = object : AdapterDataObserver() {
|
||||
override fun onChanged() {
|
||||
updateState()
|
||||
}
|
||||
|
|
@ -108,22 +55,16 @@ class RecyclerViewEmptySupport : RecyclerView {
|
|||
}
|
||||
}
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle)
|
||||
|
||||
internal fun updateState(isInitial: Boolean = false) {
|
||||
if (actualAdapter != null && !isInitial) {
|
||||
state = if (actualAdapter != null && !isInitial) {
|
||||
val emptyViewVisible = actualAdapter?.itemCount == 0
|
||||
if (emptyViewVisible) {
|
||||
state = RecyclerViewState.EMPTY
|
||||
RecyclerViewState.EMPTY
|
||||
} else {
|
||||
state = RecyclerViewState.DISPLAYING_DATA
|
||||
RecyclerViewState.DISPLAYING_DATA
|
||||
}
|
||||
} else {
|
||||
state = RecyclerViewState.LOADING
|
||||
RecyclerViewState.LOADING
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,38 +76,4 @@ class RecyclerViewEmptySupport : RecyclerView {
|
|||
actualAdapter = adapter
|
||||
updateState(true)
|
||||
}
|
||||
}
|
||||
|
||||
class FailedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val binding = FailedItemBinding.bind(itemView)
|
||||
|
||||
fun bind(onRefresh: (() -> Unit)?) {
|
||||
if (onRefresh != null) {
|
||||
binding.refreshButton.visibility = View.VISIBLE
|
||||
binding.refreshButton.setOnClickListener { onRefresh() }
|
||||
} else {
|
||||
binding.refreshButton.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val binding = EmptyItemBinding.bind(itemView)
|
||||
|
||||
fun bind(emptyItem: EmptyItem?) {
|
||||
binding.emptyIconView.setColorFilter(ContextCompat.getColor(itemView.context, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY)
|
||||
emptyItem?.iconResource?.let { binding.emptyIconView.setImageResource(it) }
|
||||
binding.emptyViewTitle.text = emptyItem?.title
|
||||
binding.emptyViewDescription.text = emptyItem?.text
|
||||
|
||||
val buttonLabel = emptyItem?.buttonLabel
|
||||
if (buttonLabel != null) {
|
||||
binding.button.visibility = View.VISIBLE
|
||||
binding.button.text = buttonLabel
|
||||
binding.button.setOnClickListener { emptyItem.onButtonTap?.invoke() }
|
||||
} else {
|
||||
binding.button.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -7,8 +7,8 @@ import android.widget.FrameLayout
|
|||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
|
||||
class HabitViewHolder(
|
||||
itemView: View,
|
||||
|
|
@ -110,15 +110,8 @@ class HabitViewHolder(
|
|||
this.btnMinus.isClickable = false
|
||||
}
|
||||
|
||||
var streakString = ""
|
||||
if (data.counterUp != null && data.counterUp ?: 0 > 0 && data.counterDown != null && data.counterDown ?: 0 > 0) {
|
||||
streakString = streakString + "+" + data.counterUp.toString() + " | -" + data.counterDown?.toString()
|
||||
} else if (data.counterUp != null && data.counterUp ?: 0 > 0) {
|
||||
streakString = streakString + "+" + data.counterUp.toString()
|
||||
} else if (data.counterDown != null && data.counterDown ?: 0 > 0) {
|
||||
streakString = streakString + "-" + data.counterDown.toString()
|
||||
}
|
||||
if (streakString.isNotEmpty()) {
|
||||
val streakString = task?.streakString
|
||||
if (streakString?.isNotEmpty() == true) {
|
||||
streakTextView.text = streakString
|
||||
streakTextView.visibility = View.VISIBLE
|
||||
streakIconView.visibility = View.VISIBLE
|
||||
|
|
|
|||
|
|
@ -4,7 +4,6 @@ import android.content.SharedPreferences
|
|||
import androidx.core.content.edit
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.habitrpg.android.habitica.api.MaintenanceApiService
|
||||
import com.habitrpg.android.habitica.components.UserComponent
|
||||
import com.habitrpg.android.habitica.data.ContentRepository
|
||||
|
|
@ -16,9 +15,10 @@ import com.habitrpg.android.habitica.helpers.TaskAlarmManager
|
|||
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager
|
||||
import com.habitrpg.android.habitica.models.TutorialStep
|
||||
import com.habitrpg.android.habitica.models.inventory.Egg
|
||||
import com.habitrpg.common.habitica.models.responses.MaintenanceResponse
|
||||
import com.habitrpg.android.habitica.proxy.AnalyticsManager
|
||||
import com.habitrpg.android.habitica.ui.TutorialView
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.habitrpg.common.habitica.models.responses.MaintenanceResponse
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import io.realm.kotlin.isValid
|
||||
|
|
@ -136,7 +136,7 @@ class MainActivityViewModel : BaseViewModel(), TutorialView.OnTutorialReaction {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
{ maintenanceResponse ->
|
||||
if (maintenanceResponse == null) {
|
||||
if (maintenanceResponse.activeMaintenance == null) {
|
||||
return@subscribe
|
||||
}
|
||||
onResult(maintenanceResponse)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,9 @@ class TaskFilterDialog(context: Context, component: UserComponent?) : HabiticaBo
|
|||
binding.secondTaskFilter.setText(R.string.dated)
|
||||
binding.thirdTaskFilter.setText(R.string.completed)
|
||||
}
|
||||
TaskType.REWARD -> {
|
||||
|
||||
}
|
||||
}
|
||||
setActiveFilter(viewModel.getActiveFilter(value))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -184,6 +184,9 @@ class TaskSerializer : JsonSerializer<Task>, JsonDeserializer<Task> {
|
|||
}
|
||||
obj.addProperty("completed", task.completed)
|
||||
}
|
||||
else -> {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return obj
|
||||
|
|
|
|||
|
|
@ -4,9 +4,10 @@ buildscript {
|
|||
ext {
|
||||
target_sdk = 32
|
||||
app_version_name = '4.0'
|
||||
app_version_code = 4010
|
||||
|
||||
|
||||
kotlin_version = '1.6.21'
|
||||
kotlin_version = '1.7.0'
|
||||
core_ktx_version = '1.8.0'
|
||||
appcompat_version = '1.4.2'
|
||||
lifecycle_version = '2.4.1'
|
||||
|
|
@ -37,7 +38,7 @@ buildscript {
|
|||
classpath "io.realm:realm-gradle-plugin:10.10.1"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.19.0"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-rc01"
|
||||
classpath "androidx.navigation:navigation-safe-args-gradle-plugin:2.5.0-rc02"
|
||||
classpath 'com.google.firebase:perf-plugin:1.4.1'
|
||||
classpath "com.google.dagger:hilt-android-gradle-plugin:$daggerhilt_version"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,118 @@
|
|||
package com.habitrpg.common.habitica.helpers
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.animation.AlphaAnimation
|
||||
import android.widget.ProgressBar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.extensions.inflate
|
||||
import com.habitrpg.common.habitica.R
|
||||
import com.habitrpg.common.habitica.databinding.EmptyItemBinding
|
||||
import com.habitrpg.common.habitica.databinding.FailedItemBinding
|
||||
|
||||
data class EmptyItem(
|
||||
var title: String,
|
||||
var text: String? = null,
|
||||
var iconResource: Int? = null,
|
||||
var buttonLabel: String? = null,
|
||||
var onButtonTap: (() -> Unit)? = null
|
||||
)
|
||||
|
||||
enum class RecyclerViewState {
|
||||
LOADING,
|
||||
EMPTY,
|
||||
DISPLAYING_DATA,
|
||||
FAILED
|
||||
}
|
||||
|
||||
class FailedViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
private val binding = FailedItemBinding.bind(itemView)
|
||||
|
||||
fun bind(onRefresh: (() -> Unit)?) {
|
||||
if (onRefresh != null) {
|
||||
binding.refreshButton.visibility = View.VISIBLE
|
||||
binding.refreshButton.setOnClickListener { onRefresh() }
|
||||
} else {
|
||||
binding.refreshButton.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class EmptyViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
|
||||
private val binding = EmptyItemBinding.bind(itemView)
|
||||
|
||||
fun bind(emptyItem: EmptyItem?) {
|
||||
binding.emptyIconView.setColorFilter(
|
||||
ContextCompat.getColor(
|
||||
itemView.context,
|
||||
R.color.text_dimmed
|
||||
), android.graphics.PorterDuff.Mode.MULTIPLY)
|
||||
emptyItem?.iconResource?.let { binding.emptyIconView.setImageResource(it) }
|
||||
binding.emptyViewTitle.text = emptyItem?.title
|
||||
binding.emptyViewDescription.text = emptyItem?.text
|
||||
|
||||
val buttonLabel = emptyItem?.buttonLabel
|
||||
if (buttonLabel != null) {
|
||||
binding.button.visibility = View.VISIBLE
|
||||
binding.button.text = buttonLabel
|
||||
binding.button.setOnClickListener { emptyItem.onButtonTap?.invoke() }
|
||||
} else {
|
||||
binding.button.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RecyclerViewStateAdapter(val showLoadingAsEmpty: Boolean = false) : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
|
||||
var onRefresh: (() -> Unit)? = null
|
||||
var emptyItem: EmptyItem? = null
|
||||
set(value) {
|
||||
field = value
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
|
||||
var state: RecyclerViewState = RecyclerViewState.LOADING
|
||||
set(value) {
|
||||
field = value
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
0 -> {
|
||||
val view = parent.inflate(R.layout.loading_item)
|
||||
val animation1 = AlphaAnimation(0.0f, 1.0f)
|
||||
animation1.duration = 300
|
||||
animation1.startOffset = 500
|
||||
animation1.fillAfter = true
|
||||
view.findViewById<ProgressBar>(R.id.loading_indicator).startAnimation(animation1)
|
||||
object : RecyclerView.ViewHolder(view) {}
|
||||
}
|
||||
1 -> FailedViewHolder(parent.inflate(R.layout.failed_item))
|
||||
else -> EmptyViewHolder(parent.inflate(R.layout.empty_item))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is EmptyViewHolder) {
|
||||
holder.bind(emptyItem)
|
||||
} else if (holder is FailedViewHolder) {
|
||||
holder.bind(onRefresh)
|
||||
}
|
||||
(holder as? EmptyViewHolder)?.bind(emptyItem)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return 1
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return when {
|
||||
state == RecyclerViewState.LOADING && !showLoadingAsEmpty -> 0
|
||||
state == RecyclerViewState.FAILED -> 1
|
||||
else -> 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1,10 +0,0 @@
|
|||
package com.habitrpg.common.habitica.models.responses;
|
||||
|
||||
public class MaintenanceResponse {
|
||||
|
||||
public Boolean activeMaintenance;
|
||||
public Integer minBuild;
|
||||
public String title;
|
||||
public String imageUrl;
|
||||
public String description;
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
package com.habitrpg.common.habitica.models.responses
|
||||
|
||||
class MaintenanceResponse {
|
||||
var activeMaintenance: Boolean? = null
|
||||
var minBuild: Int? = null
|
||||
var title: String? = null
|
||||
var imageUrl: String? = null
|
||||
var description: String? = null
|
||||
}
|
||||
|
|
@ -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 {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,15 @@
|
|||
package com.habitrpg.common.habitica.models.tasks
|
||||
|
||||
class TasksOrder {
|
||||
fun positionOf(key: String, type: TaskType): Int {
|
||||
return when (type) {
|
||||
TaskType.HABIT -> habits.indexOf(key)
|
||||
TaskType.DAILY -> dailys.indexOf(key)
|
||||
TaskType.TODO -> todos.indexOf(key)
|
||||
TaskType.REWARD -> rewards.indexOf(key)
|
||||
}
|
||||
}
|
||||
|
||||
var habits: List<String> = listOf()
|
||||
var dailys: List<String> = listOf()
|
||||
var todos: List<String> = listOf()
|
||||
|
|
|
|||
|
|
@ -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(
|
||||
|
|
|
|||
16
common/src/main/res/drawable-anydpi/failed_loading.xml
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24"
|
||||
android:tint="#FFFFFF"
|
||||
android:alpha="0.8">
|
||||
<group android:scaleX="1.2"
|
||||
android:scaleY="1.2"
|
||||
android:translateX="-2.4"
|
||||
android:translateY="-2.4">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M11,15h2v2h-2zM11,7h2v6h-2zM11.99,2C6.47,2 2,6.48 2,12s4.47,10 9.99,10C17.52,22 22,17.52 22,12S17.52,2 11.99,2zM12,20c-4.42,0 -8,-3.58 -8,-8s3.58,-8 8,-8 8,3.58 8,8 -3.58,8 -8,8z"/>
|
||||
</group>
|
||||
</vector>
|
||||
BIN
common/src/main/res/drawable-hdpi/failed_loading.png
Normal file
|
After Width: | Height: | Size: 577 B |
|
Before Width: | Height: | Size: 214 B After Width: | Height: | Size: 214 B |
|
Before Width: | Height: | Size: 660 B After Width: | Height: | Size: 660 B |
|
Before Width: | Height: | Size: 189 B After Width: | Height: | Size: 189 B |
|
Before Width: | Height: | Size: 262 B After Width: | Height: | Size: 262 B |
|
Before Width: | Height: | Size: 211 B After Width: | Height: | Size: 211 B |
BIN
common/src/main/res/drawable-mdpi/failed_loading.png
Normal file
|
After Width: | Height: | Size: 424 B |
|
Before Width: | Height: | Size: 159 B After Width: | Height: | Size: 159 B |
|
Before Width: | Height: | Size: 479 B After Width: | Height: | Size: 479 B |
|
Before Width: | Height: | Size: 168 B After Width: | Height: | Size: 168 B |
|
Before Width: | Height: | Size: 194 B After Width: | Height: | Size: 194 B |
|
Before Width: | Height: | Size: 142 B After Width: | Height: | Size: 142 B |
BIN
common/src/main/res/drawable-xhdpi/failed_loading.png
Normal file
|
After Width: | Height: | Size: 904 B |
|
Before Width: | Height: | Size: 217 B After Width: | Height: | Size: 217 B |
|
Before Width: | Height: | Size: 887 B After Width: | Height: | Size: 887 B |
|
Before Width: | Height: | Size: 228 B After Width: | Height: | Size: 228 B |
|
Before Width: | Height: | Size: 320 B After Width: | Height: | Size: 320 B |
|
Before Width: | Height: | Size: 230 B After Width: | Height: | Size: 230 B |
BIN
common/src/main/res/drawable-xxhdpi/failed_loading.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
|
Before Width: | Height: | Size: 291 B After Width: | Height: | Size: 291 B |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 1.2 KiB |
|
Before Width: | Height: | Size: 284 B After Width: | Height: | Size: 284 B |
|
Before Width: | Height: | Size: 453 B After Width: | Height: | Size: 453 B |
|
Before Width: | Height: | Size: 322 B After Width: | Height: | Size: 322 B |
|
|
@ -26,10 +26,10 @@
|
|||
|
||||
<TextView
|
||||
android:id="@+id/emptyViewDescription"
|
||||
style="@style/Body2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:textSize="14sp"
|
||||
android:textColor="@color/text_ternary"
|
||||
tools:text="No Items" />
|
||||
|
||||
|
|
@ -37,7 +37,6 @@
|
|||
android:id="@+id/button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/HabiticaButton.Primary"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible"
|
||||
android:layout_marginTop="@dimen/spacing_large"/>
|
||||
|
|
@ -2,24 +2,23 @@
|
|||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
<androidx.constraintlayout.utils.widget.ImageFilterView
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:src="@drawable/baseline_error_outline_black_36dp"
|
||||
android:tint="@color/text_quad"/>
|
||||
android:src="@drawable/failed_loading"
|
||||
app:tint="@color/text_quad"/>
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:text="@string/failed"
|
||||
style="@style/Headline"
|
||||
android:textColor="@color/text_primary"/>
|
||||
<Button
|
||||
android:id="@+id/refresh_button"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
style="@style/HabiticaButton.Primary"
|
||||
android:text="@string/retry"
|
||||
android:layout_marginTop="@dimen/spacing_large" />
|
||||
</LinearLayout>
|
||||
|
|
@ -12,4 +12,7 @@
|
|||
<dimen name="spacing_xlarge">32dp</dimen>
|
||||
|
||||
<dimen name="icon_size">18dp</dimen>
|
||||
|
||||
<dimen name="card_medium_text">18.0sp</dimen>
|
||||
<dimen name="card_small_text">14.0sp</dimen>
|
||||
</resources>
|
||||
|
|
@ -27,6 +27,7 @@
|
|||
<string name="settings">Settings</string>
|
||||
<string name="new_task">New Task</string>
|
||||
<string name="avatar">Avatar</string>
|
||||
<string name="user_level">Lvl. %d</string>
|
||||
|
||||
<string name="action_edit">Edit</string>
|
||||
<string name="action_cancel">Cancel</string>
|
||||
|
|
@ -54,6 +55,12 @@
|
|||
<string name="create_task_title">Create a Task</string>
|
||||
<string name="complete_task_title">Complete a Task</string>
|
||||
<string name="create_task">Create %s</string>
|
||||
<string name="new_task_x">New %s</string>
|
||||
<string name="save_task_x">Save %s</string>
|
||||
|
||||
<string name="are_you_sure">Are you sure?</string>
|
||||
<string name="task">Task</string>
|
||||
|
||||
<string name="failed">Failed to load</string>
|
||||
<string name="retry">Retry</string>
|
||||
</resources>
|
||||
10
habitica.properties.example
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# Habitica Server Port
|
||||
# local instance
|
||||
# PORT=3000
|
||||
PORT=80
|
||||
BASE_URL=https://habitica.com
|
||||
STAGING_KEY=
|
||||
ANDROID_TESTING_UUID=
|
||||
APPLE_AUTH_CLIENT_ID=
|
||||
DEBUG_USER_ID=
|
||||
DEBUG_API_KEY=
|
||||
4
habitica.resources.example
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
fabric_key=
|
||||
facebook_app_id=
|
||||
amplitude_app_id=
|
||||
application_ad_id=
|
||||
|
|
@ -13,7 +13,7 @@ android {
|
|||
applicationId "com.habitrpg.android.habitica"
|
||||
minSdk 26
|
||||
targetSdk target_sdk
|
||||
versionCode 4001
|
||||
versionCode app_version_code + 1
|
||||
versionName app_version_name
|
||||
|
||||
}
|
||||
|
|
@ -79,6 +79,8 @@ dependencies {
|
|||
implementation "com.google.dagger:hilt-android:$daggerhilt_version"
|
||||
kapt "com.google.dagger:hilt-compiler:$daggerhilt_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
|
||||
|
||||
}
|
||||
repositories {
|
||||
mavenCentral()
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.habitrpg.android.habitica">
|
||||
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
<uses-permission android:name="android.permission.WAKE_LOCK" />
|
||||
|
||||
<uses-feature android:name="android.hardware.type.watch" />
|
||||
|
|
|
|||
|
|
@ -5,11 +5,13 @@ import android.content.Intent
|
|||
import com.habitrpg.common.habitica.extensions.setupCoil
|
||||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import com.habitrpg.common.habitica.views.HabiticaIconsHelper
|
||||
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
|
||||
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
|
||||
import com.habitrpg.wearos.habitica.ui.activities.BaseActivity
|
||||
import com.habitrpg.wearos.habitica.ui.activities.FaintActivity
|
||||
import com.habitrpg.wearos.habitica.ui.activities.RYAActivity
|
||||
import dagger.hilt.android.HiltAndroidApp
|
||||
import kotlinx.coroutines.CoroutineExceptionHandler
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.flow.collect
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
|
|
@ -20,6 +22,7 @@ import javax.inject.Inject
|
|||
class MainApplication : Application() {
|
||||
|
||||
@Inject lateinit var userRepository: UserRepository
|
||||
@Inject lateinit var taskRepository: TaskRepository
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
|
@ -40,5 +43,13 @@ class MainApplication : Application() {
|
|||
}
|
||||
}.collect()
|
||||
}
|
||||
if (userRepository.hasAuthentication) {
|
||||
MainScope().launch(CoroutineExceptionHandler { coroutineContext, throwable ->
|
||||
|
||||
}) {
|
||||
val user = userRepository.retrieveUser()
|
||||
taskRepository.retrieveTasks(user?.tasksOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,6 +1,8 @@
|
|||
package com.habitrpg.wearos.habitica.data
|
||||
|
||||
import android.content.Context
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.NetworkCapabilities
|
||||
import com.amplitude.api.Amplitude
|
||||
import com.habitrpg.common.habitica.BuildConfig
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
|
|
@ -10,6 +12,7 @@ import com.habitrpg.common.habitica.models.auth.UserAuthSocial
|
|||
import com.habitrpg.wearos.habitica.models.WearableHabitResponse
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import okhttp3.Cache
|
||||
import okhttp3.CacheControl
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
import okhttp3.logging.HttpLoggingInterceptor
|
||||
|
|
@ -34,7 +37,21 @@ class ApiClient @Inject constructor(
|
|||
buildRetrofit()
|
||||
}
|
||||
|
||||
fun buildRetrofit() {
|
||||
private fun hasNetwork(context: Context): Boolean {
|
||||
val connectivityManager =
|
||||
context.getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager
|
||||
val networkCapabilities = connectivityManager.activeNetwork ?: return false
|
||||
val actNw =
|
||||
connectivityManager.getNetworkCapabilities(networkCapabilities) ?: return false
|
||||
return when {
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> true
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> true
|
||||
actNw.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildRetrofit() {
|
||||
val logging = HttpLoggingInterceptor()
|
||||
if (BuildConfig.DEBUG) {
|
||||
logging.level = HttpLoggingInterceptor.Level.BODY
|
||||
|
|
@ -44,15 +61,36 @@ class ApiClient @Inject constructor(
|
|||
|
||||
val calendar = GregorianCalendar()
|
||||
val timeZone = calendar.timeZone
|
||||
val timezoneOffset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.timeInMillis).toLong(), TimeUnit.MILLISECONDS)
|
||||
|
||||
val cacheSize: Long = 10 * 1024 * 1024 // 10 MB
|
||||
val timezoneOffset = -TimeUnit.MINUTES.convert(
|
||||
timeZone.getOffset(calendar.timeInMillis).toLong(),
|
||||
TimeUnit.MILLISECONDS
|
||||
)
|
||||
|
||||
val cacheSize = (5 * 1024 * 1024).toLong()
|
||||
val cache = Cache(File(context.cacheDir, "http_cache"), cacheSize)
|
||||
|
||||
val client = OkHttpClient.Builder()
|
||||
.cache(cache)
|
||||
.addInterceptor(logging)
|
||||
.addInterceptor { chain ->
|
||||
val request = chain.request()
|
||||
var cacheContol = CacheControl.Builder()
|
||||
cacheContol = if (request.method == "GET") {
|
||||
if (hasNetwork(context)) {
|
||||
cacheContol.maxAge(5, TimeUnit.MINUTES)
|
||||
} else {
|
||||
cacheContol.maxAge(1, TimeUnit.DAYS)
|
||||
.onlyIfCached()
|
||||
}
|
||||
} else {
|
||||
cacheContol.noCache()
|
||||
.noStore()
|
||||
}
|
||||
chain.proceed(request.newBuilder().header(
|
||||
"Cache-Control",
|
||||
cacheContol.build().toString()
|
||||
).build())
|
||||
}
|
||||
.addNetworkInterceptor { chain ->
|
||||
val original = chain.request()
|
||||
var builder: Request.Builder = original.newBuilder()
|
||||
|
|
@ -70,8 +108,16 @@ class ApiClient @Inject constructor(
|
|||
builder = builder.header("Authorization", "Basic " + BuildConfig.STAGING_KEY)
|
||||
}
|
||||
val request = builder.method(original.method, original.body)
|
||||
.removeHeader("Pragma")
|
||||
.build()
|
||||
chain.proceed(request)
|
||||
val response = chain.proceed(request)
|
||||
if (request.method == "GET") {
|
||||
response.newBuilder()
|
||||
.header("Cache-Control", request.header("Cache-Control") ?: "")
|
||||
.build()
|
||||
} else {
|
||||
response
|
||||
}
|
||||
}
|
||||
.readTimeout(2400, TimeUnit.SECONDS)
|
||||
.build()
|
||||
|
|
@ -111,6 +157,9 @@ class ApiClient @Inject constructor(
|
|||
suspend fun runCron() = process(apiService.runCron())
|
||||
|
||||
suspend fun getTasks() = process(apiService.getTasks())
|
||||
suspend fun scoreTask(id: String, direction: String) = process(apiService.scoreTask(id, direction))
|
||||
suspend fun scoreTask(id: String, direction: String) =
|
||||
process(apiService.scoreTask(id, direction))
|
||||
|
||||
suspend fun createTask(task: Task) = process(apiService.createTask(task))
|
||||
fun hasAuthentication() = hostConfig.hasAuthentication()
|
||||
}
|
||||
|
|
@ -3,11 +3,14 @@ package com.habitrpg.wearos.habitica.data.repositories
|
|||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.asFlow
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
import com.habitrpg.common.habitica.models.tasks.TasksOrder
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.models.tasks.TaskList
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import kotlinx.coroutines.flow.map
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
|
|
@ -19,25 +22,45 @@ class TaskLocalRepository @Inject constructor() {
|
|||
TaskType.TODO to MutableLiveData<List<Task>>(),
|
||||
TaskType.REWARD to MutableLiveData<List<Task>>()
|
||||
)
|
||||
|
||||
private val taskCountHelperValue = MutableLiveData<Long>()
|
||||
|
||||
fun getTasks(type: TaskType): Flow<List<Task>> {
|
||||
return tasks[type]?.asFlow() ?: emptyFlow()
|
||||
}
|
||||
|
||||
fun saveTasks(tasks: TaskList) {
|
||||
fun saveTasks(tasks: TaskList, order: TasksOrder?) {
|
||||
val taskMap = mutableMapOf(
|
||||
TaskType.HABIT to mutableListOf<Task>(),
|
||||
TaskType.DAILY to mutableListOf<Task>(),
|
||||
TaskType.TODO to mutableListOf<Task>(),
|
||||
TaskType.REWARD to mutableListOf<Task>()
|
||||
TaskType.HABIT to sortTasks(tasks.tasks, order?.habits ?: emptyList(), TaskType.HABIT),
|
||||
TaskType.DAILY to sortTasks(tasks.tasks, order?.dailys ?: emptyList(), TaskType.DAILY),
|
||||
TaskType.TODO to sortTasks(tasks.tasks, order?.todos ?: emptyList(), TaskType.TODO),
|
||||
TaskType.REWARD to sortTasks(tasks.tasks, order?.rewards ?: emptyList(), TaskType.REWARD)
|
||||
)
|
||||
for (task in tasks.tasks) {
|
||||
if (task.value.type != null) {
|
||||
taskMap[task.value.type]?.add(task.value)
|
||||
}
|
||||
}
|
||||
for (type in taskMap) {
|
||||
this.tasks[type.key]?.value = type.value
|
||||
}
|
||||
taskCountHelperValue.value = Date().time
|
||||
}
|
||||
|
||||
private fun sortTasks(taskMap: MutableMap<String, Task>, taskOrder: List<String>, type: TaskType): List<Task> {
|
||||
val taskList = ArrayList<Task>()
|
||||
var position = 0
|
||||
for (taskId in taskOrder) {
|
||||
val task = taskMap[taskId]
|
||||
if (task != null) {
|
||||
task.position = position
|
||||
taskList.add(task)
|
||||
position++
|
||||
taskMap.remove(taskId)
|
||||
}
|
||||
}
|
||||
for (task in taskMap.values) {
|
||||
if (task.type != type) continue
|
||||
task.position = position
|
||||
taskList.add(task)
|
||||
position++
|
||||
}
|
||||
return taskList
|
||||
}
|
||||
|
||||
fun updateTask(task: Task) {
|
||||
|
|
@ -51,6 +74,7 @@ class TaskLocalRepository @Inject constructor() {
|
|||
oldList?.let {
|
||||
tasks[task.type]?.value = it
|
||||
}
|
||||
taskCountHelperValue.value = Date().time
|
||||
}
|
||||
|
||||
fun getTask(taskID: String): Flow<Task?> {
|
||||
|
|
@ -63,10 +87,23 @@ class TaskLocalRepository @Inject constructor() {
|
|||
return emptyFlow()
|
||||
}
|
||||
|
||||
fun getTaskCounts() = flowOf(mapOf(
|
||||
fun getTaskCounts() = taskCountHelperValue.asFlow().map {
|
||||
mapOf(
|
||||
TaskType.HABIT.value to (tasks[TaskType.HABIT]?.value?.size ?: 0),
|
||||
TaskType.DAILY.value to (tasks[TaskType.DAILY]?.value?.size ?: 0),
|
||||
TaskType.TODO.value to (tasks[TaskType.TODO]?.value?.size ?: 0),
|
||||
TaskType.REWARD.value to (tasks[TaskType.REWARD]?.value?.size ?: 0),
|
||||
))
|
||||
)
|
||||
}
|
||||
|
||||
fun getActiveTaskCounts() = taskCountHelperValue.asFlow().map {
|
||||
mapOf(
|
||||
TaskType.HABIT.value to (tasks[TaskType.HABIT]?.value?.size ?: 0),
|
||||
TaskType.DAILY.value to (tasks[TaskType.DAILY]?.value?.filter { it.isDue == true && !it.completed }?.size
|
||||
?: 0),
|
||||
TaskType.TODO.value to (tasks[TaskType.TODO]?.value?.filter { !it.completed }?.size
|
||||
?: 0),
|
||||
TaskType.REWARD.value to (tasks[TaskType.REWARD]?.value?.size ?: 0),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -3,6 +3,7 @@ package com.habitrpg.wearos.habitica.data.repositories
|
|||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
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.wearos.habitica.data.ApiClient
|
||||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.models.tasks.TaskList
|
||||
|
|
@ -11,11 +12,11 @@ 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? {
|
||||
suspend fun retrieveTasks(order: TasksOrder?): TaskList? {
|
||||
val tasks = apiClient.getTasks()
|
||||
tasks?.let { localRepository.saveTasks(tasks) }
|
||||
tasks?.let { localRepository.saveTasks(tasks, order) }
|
||||
return tasks
|
||||
}
|
||||
fun getTasks(taskType: TaskType) = localRepository.getTasks(taskType)
|
||||
|
|
@ -25,9 +26,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?> {
|
||||
|
|
@ -43,4 +71,5 @@ class TaskRepository @Inject constructor(val apiClient: ApiClient, val localRepo
|
|||
}
|
||||
|
||||
fun getTaskCounts() = localRepository.getTaskCounts()
|
||||
fun getActiveTaskCounts() = localRepository.getActiveTaskCounts()
|
||||
}
|
||||
|
|
@ -5,7 +5,8 @@ import com.habitrpg.wearos.habitica.models.user.User
|
|||
import javax.inject.Inject
|
||||
|
||||
class UserRepository @Inject constructor(val apiClient: ApiClient, val localRepository: UserLocalRepository) {
|
||||
|
||||
val hasAuthentication: Boolean
|
||||
get() = apiClient.hasAuthentication()
|
||||
fun getUser() = localRepository.getUser()
|
||||
|
||||
suspend fun retrieveUser(): User? {
|
||||
|
|
@ -21,5 +22,8 @@ class UserRepository @Inject constructor(val apiClient: ApiClient, val localRepo
|
|||
|
||||
suspend fun sleep() = apiClient.sleep()
|
||||
suspend fun revive() = apiClient.revive()
|
||||
suspend fun runCron() = apiClient.runCron()
|
||||
suspend fun runCron() {
|
||||
apiClient.runCron()
|
||||
retrieveUser()
|
||||
}
|
||||
}
|
||||
|
|
@ -92,6 +92,21 @@ open class Task constructor(): Parcelable {
|
|||
val completedChecklistCount: Int
|
||||
get() = checklist?.count { it.completed } ?: 0
|
||||
|
||||
val streakString: String?
|
||||
get() {
|
||||
return if (counterUp != null && (counterUp ?: 0) > 0 && counterDown != null && (counterDown ?: 0) > 0) {
|
||||
"+" + counterUp.toString() + " | -" + counterDown?.toString()
|
||||
} else if (counterUp != null && (counterUp ?: 0) > 0) {
|
||||
"+" + counterUp.toString()
|
||||
} else if (counterDown != null && (counterDown ?: 0) > 0) {
|
||||
"-" + counterDown.toString()
|
||||
} else if ((streak ?: 0) > 0) {
|
||||
return streak.toString()
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
val extraLightTaskColor: Int
|
||||
get() {
|
||||
return when {
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ data class MenuItem(
|
|||
val color: Int,
|
||||
val textColor: Int,
|
||||
val isProminent: Boolean = false,
|
||||
val isHidden: Boolean = false,
|
||||
var detailText: String? = null,
|
||||
val onClick: () -> Unit
|
||||
)
|
||||
|
|
@ -1,10 +1,12 @@
|
|||
package com.habitrpg.wearos.habitica.models.user
|
||||
|
||||
import com.habitrpg.common.habitica.models.Avatar
|
||||
import com.habitrpg.common.habitica.models.tasks.TasksOrder
|
||||
import com.squareup.moshi.JsonClass
|
||||
|
||||
@JsonClass(generateAdapter = true)
|
||||
class User: Avatar {
|
||||
val tasksOrder: TasksOrder? = null
|
||||
val isDead: Boolean
|
||||
get() = (stats?.hp ?: 0.0) <= 0.0
|
||||
override val currentMount: String?
|
||||
|
|
|
|||
|
|
@ -43,15 +43,6 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
|
|||
) {
|
||||
openTaskFormActivity()
|
||||
},
|
||||
MenuItem(
|
||||
TaskType.HABIT.value,
|
||||
getString(R.string.habits),
|
||||
AppCompatResources.getDrawable(this, R.drawable.icon_habits),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_200),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_700)
|
||||
) {
|
||||
openTasklist(TaskType.HABIT)
|
||||
},
|
||||
MenuItem(
|
||||
TaskType.DAILY.value,
|
||||
getString(R.string.dailies),
|
||||
|
|
@ -70,17 +61,27 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
|
|||
) {
|
||||
openTasklist(TaskType.TODO)
|
||||
},
|
||||
MenuItem(
|
||||
TaskType.HABIT.value,
|
||||
getString(R.string.habits),
|
||||
AppCompatResources.getDrawable(this, R.drawable.icon_habits),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_200),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_700)
|
||||
) {
|
||||
openTasklist(TaskType.HABIT)
|
||||
},
|
||||
MenuItem(
|
||||
TaskType.REWARD.value,
|
||||
getString(R.string.rewards),
|
||||
AppCompatResources.getDrawable(this, R.drawable.icon_rewards),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_200),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_700)
|
||||
ContextCompat.getColor(this, R.color.watch_purple_700),
|
||||
isHidden = true
|
||||
) {
|
||||
openTasklist(TaskType.REWARD)
|
||||
},
|
||||
MenuItem(
|
||||
"Stats",
|
||||
"stats",
|
||||
getString(R.string.stats),
|
||||
AppCompatResources.getDrawable(this, R.drawable.icon_stats),
|
||||
ContextCompat.getColor(this, R.color.watch_purple_200),
|
||||
|
|
@ -110,13 +111,18 @@ class MainActivity : BaseActivity<ActivityMainBinding, MainViewModel>() {
|
|||
viewModel.user.observe(this) {
|
||||
adapter.title = it.profile?.name ?: ""
|
||||
adapter.notifyItemChanged(0)
|
||||
val index = adapter.data.indexOfFirst { it.identifier == "stats" }
|
||||
adapter.data[index].detailText = getString(R.string.user_level, it.stats?.lvl ?: 0)
|
||||
adapter.notifyItemChanged(index+1)
|
||||
}
|
||||
viewModel.taskCounts.observe(this) {
|
||||
adapter.data.forEach { menuItem ->
|
||||
if (it.containsKey(menuItem.identifier) && it[menuItem.identifier]!! > 0) {
|
||||
menuItem.detailText = it[menuItem.identifier].toString()
|
||||
} else {
|
||||
menuItem.detailText = null
|
||||
if (it.containsKey(menuItem.identifier)) {
|
||||
if (it[menuItem.identifier]!! > 0) {
|
||||
menuItem.detailText = it[menuItem.identifier].toString()
|
||||
} else {
|
||||
menuItem.detailText = null
|
||||
}
|
||||
}
|
||||
}
|
||||
adapter.notifyDataSetChanged()
|
||||
|
|
|
|||
|
|
@ -69,17 +69,10 @@ class SettingsActivity: BaseActivity<ActivitySettingsBinding, SettingsViewModel>
|
|||
adapter.data[index].value = viewModel.isTaskResultHidden()
|
||||
adapter.notifyItemChanged(index)
|
||||
},
|
||||
SettingsItem(
|
||||
"spacer",
|
||||
getString(R.string.settings),
|
||||
SettingsItem.Types.SPACER,
|
||||
null
|
||||
) {
|
||||
},
|
||||
SettingsItem(
|
||||
"logout",
|
||||
getString(R.string.logout),
|
||||
SettingsItem.Types.DESTRUCTIVE_BUTTON,
|
||||
SettingsItem.Types.BUTTON,
|
||||
null
|
||||
) {
|
||||
showLogoutConfirmation()
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,13 +1,16 @@
|
|||
package com.habitrpg.wearos.habitica.ui.activities
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.os.Bundle
|
||||
import android.view.inputmethod.EditorInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import android.widget.TextView
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.postDelayed
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityTaskFormBinding
|
||||
|
|
@ -25,7 +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.create_task, value?.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()
|
||||
|
||||
|
|
@ -72,13 +83,24 @@ class TaskFormActivity : BaseActivity<ActivityTaskFormBinding, TaskFormViewModel
|
|||
binding.taskTypeHeader.isVisible = false
|
||||
binding.taskTypeWrapper.isVisible = false
|
||||
binding.header.textView.text = getString(R.string.create_task, taskType?.value)
|
||||
binding.confirmationTitle.text = getString(R.string.create_task, taskType?.value)
|
||||
binding.editText.requestFocus()
|
||||
} else {
|
||||
taskType = TaskType.TODO
|
||||
binding.header.textView.text = getString(R.string.create_task_title)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (binding.editText.hasFocus()) {
|
||||
binding.editText.postDelayed(100) {
|
||||
val imm: InputMethodManager =
|
||||
getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||
imm.showSoftInput(binding.editText, InputMethodManager.SHOW_FORCED)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateTaskTypeButton(button: TextView, thisType: TaskType) {
|
||||
if (taskType == thisType) {
|
||||
button.backgroundTintList =
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import androidx.activity.viewModels
|
|||
import androidx.wear.widget.WearableLinearLayoutManager
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityTasklistBinding
|
||||
import com.habitrpg.common.habitica.helpers.EmptyItem
|
||||
import com.habitrpg.common.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.common.habitica.models.responses.TaskScoringResult
|
||||
import com.habitrpg.common.habitica.models.tasks.TaskType
|
||||
|
|
@ -32,6 +33,18 @@ class TaskListActivity: BaseActivity<ActivityTasklistBinding, TaskListViewModel>
|
|||
layoutManager =
|
||||
WearableLinearLayoutManager(this@TaskListActivity, HabiticaScrollingLayoutCallback())
|
||||
adapter = this@TaskListActivity.adapter
|
||||
emptyItem = EmptyItem(
|
||||
getString(R.string.no_tasks, getString(when(viewModel.taskType) {
|
||||
TaskType.HABIT -> R.string.habit
|
||||
TaskType.DAILY -> R.string.daily
|
||||
TaskType.TODO -> R.string.todo
|
||||
TaskType.REWARD -> R.string.reward
|
||||
else -> R.string.task
|
||||
}))
|
||||
)
|
||||
onRefresh = {
|
||||
viewModel.retrieveTasks()
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.tasks.observe(this) {
|
||||
|
|
@ -46,10 +59,6 @@ class TaskListActivity: BaseActivity<ActivityTasklistBinding, TaskListViewModel>
|
|||
openTaskDetailActivity(it)
|
||||
}
|
||||
|
||||
viewModel.user.observe(this) {
|
||||
|
||||
}
|
||||
|
||||
binding.addTaskButton.setOnClickListener { openTaskFormActivity() }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -90,7 +90,7 @@ class SettingsViewHolder(itemView: View) : BindableViewHolder<SettingsItem>(item
|
|||
|
||||
if (data.value as? Boolean == true) {
|
||||
binding.row.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.watch_purple_100))
|
||||
binding.row.background.alpha = 127
|
||||
binding.row.background.alpha = 102
|
||||
} else {
|
||||
binding.row.backgroundTintList = ColorStateList.valueOf(ContextCompat.getColor(itemView.context, R.color.watch_purple_5))
|
||||
binding.row.background.alpha = 255
|
||||
|
|
|
|||
|
|
@ -19,9 +19,11 @@ class HubViewHolder(itemView: View): BindableViewHolder<MenuItem>(itemView) {
|
|||
if (data.isProminent) {
|
||||
binding.iconView.setColorFilter(ContextCompat.getColor(itemView.context, R.color.black))
|
||||
binding.rowContainer.backgroundTintList = ColorStateList.valueOf(data.color)
|
||||
binding.detailView.setTextColor(data.textColor)
|
||||
} else {
|
||||
binding.iconView.setColorFilter(data.color)
|
||||
binding.rowContainer.backgroundTintList = ContextCompat.getColorStateList(itemView.context, R.color.surface)
|
||||
binding.detailView.setTextColor(ContextCompat.getColor(itemView.context, R.color.watch_purple_200))
|
||||
}
|
||||
binding.root.setOnClickListener {
|
||||
data.onClick()
|
||||
|
|
|
|||
|
|
@ -3,12 +3,12 @@ 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 com.habitrpg.android.habitica.databinding.RowDailyBinding
|
||||
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
|
||||
|
|
|
|||
|
|
@ -2,15 +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.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 {
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
|
|
@ -1,13 +1,11 @@
|
|||
package com.habitrpg.wearos.habitica.ui.viewmodels
|
||||
|
||||
import androidx.lifecycle.asLiveData
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.habitrpg.wearos.habitica.data.repositories.TaskRepository
|
||||
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
|
||||
import com.habitrpg.wearos.habitica.managers.LoadingManager
|
||||
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
|
|
@ -16,15 +14,6 @@ class MainViewModel @Inject constructor(
|
|||
private val taskRepository: TaskRepository,
|
||||
exceptionBuilder: ExceptionHandlerBuilder, loadingManager: LoadingManager
|
||||
) : BaseViewModel(userRepository, exceptionBuilder, loadingManager) {
|
||||
val taskCounts = taskRepository.getTaskCounts().asLiveData()
|
||||
val taskCounts = taskRepository.getActiveTaskCounts().asLiveData()
|
||||
val user = userRepository.getUser().asLiveData()
|
||||
|
||||
init {
|
||||
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
|
||||
loadingManager.startLoading()
|
||||
userRepository.retrieveUser()
|
||||
taskRepository.retrieveTasks()
|
||||
loadingManager.endLoading()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -31,8 +31,8 @@ class SettingsViewModel @Inject constructor(userRepository: UserRepository,
|
|||
fun resyncData() {
|
||||
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
|
||||
loadingManager.startLoading()
|
||||
userRepository.retrieveUser()
|
||||
taskRepository.retrieveTasks()
|
||||
val user = userRepository.retrieveUser()
|
||||
taskRepository.retrieveTasks(user?.tasksOrder)
|
||||
loadingManager.endLoading()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,7 +5,6 @@ import androidx.core.content.edit
|
|||
import androidx.lifecycle.viewModelScope
|
||||
import com.google.android.gms.wearable.MessageClient
|
||||
import com.google.android.gms.wearable.MessageEvent
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.habitrpg.common.habitica.helpers.KeyHelper
|
||||
import com.habitrpg.wearos.habitica.data.ApiClient
|
||||
import com.habitrpg.wearos.habitica.data.repositories.UserRepository
|
||||
|
|
@ -18,7 +17,6 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class SplashViewModel @Inject constructor(userRepository: UserRepository,
|
||||
exceptionBuilder: ExceptionHandlerBuilder,
|
||||
val hostConfig: HostConfig,
|
||||
val apiClient: ApiClient,
|
||||
val sharedPreferences: SharedPreferences,
|
||||
val keyHelper: KeyHelper?, loadingManager: LoadingManager
|
||||
|
|
@ -26,7 +24,7 @@ class SplashViewModel @Inject constructor(userRepository: UserRepository,
|
|||
lateinit var onLoginCompleted: (Boolean) -> Unit
|
||||
val hasAuthentication: Boolean
|
||||
get() {
|
||||
return hostConfig.hasAuthentication()
|
||||
return apiClient.hasAuthentication()
|
||||
}
|
||||
|
||||
override fun onMessageReceived(event: MessageEvent) {
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import com.habitrpg.wearos.habitica.managers.LoadingManager
|
|||
import com.habitrpg.wearos.habitica.models.tasks.Task
|
||||
import com.habitrpg.wearos.habitica.util.ExceptionHandlerBuilder
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -24,14 +25,24 @@ class TaskListViewModel @Inject constructor(
|
|||
) : BaseViewModel(userRepository, exceptionBuilder, loadingManager) {
|
||||
val taskType = TaskType.from(savedStateHandle.get<String>("task_type"))
|
||||
val tasks = taskRepository.getTasks(taskType ?: TaskType.HABIT).asLiveData()
|
||||
val user = userRepository.getUser()
|
||||
val user = userRepository.getUser()
|
||||
.asLiveData()
|
||||
|
||||
fun scoreTask(task: Task, direction: TaskDirection, onResult: (TaskScoringResult?) -> Unit) {
|
||||
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
|
||||
val result = taskRepository.scoreTask(user.value, task, direction)
|
||||
val result = taskRepository.scoreTask(
|
||||
userRepository.localRepository.getUser().first(),
|
||||
task,
|
||||
direction
|
||||
)
|
||||
onResult(result)
|
||||
}
|
||||
}
|
||||
|
||||
fun retrieveTasks() {
|
||||
viewModelScope.launch(exceptionBuilder.userFacing(this)) {
|
||||
val user = userRepository.retrieveUser()
|
||||
taskRepository.retrieveTasks(user?.tasksOrder)
|
||||
}
|
||||
}
|
||||
}
|
||||