fix formatting

This commit is contained in:
Phillip Thelen 2023-02-13 14:24:37 +01:00
parent c9fea5872a
commit afb2b234d9
329 changed files with 1782 additions and 1622 deletions

View file

@ -87,7 +87,7 @@ jobs:
- name: Run with Gradle - name: Run with Gradle
uses: gradle/gradle-build-action@v2 uses: gradle/gradle-build-action@v2
with: with:
arguments: ktlint arguments: ktlintCheck
detekt: detekt:
runs-on: ubuntu-latest runs-on: ubuntu-latest

View file

@ -21,10 +21,6 @@ repositories {
maven { url "https://jitpack.io" } maven { url "https://jitpack.io" }
} }
configurations {
ktlint
}
dependencies { dependencies {
implementation fileTree(include: ['*.jar'], dir: '../common/libs') implementation fileTree(include: ['*.jar'], dir: '../common/libs')
//Networking //Networking
@ -42,15 +38,15 @@ dependencies {
compileOnly 'javax.annotation:javax.annotation-api:1.3.2' compileOnly 'javax.annotation:javax.annotation-api:1.3.2'
//App Compatibility and Material Design //App Compatibility and Material Design
implementation "androidx.appcompat:appcompat:$appcompat_version" implementation "androidx.appcompat:appcompat:$appcompat_version"
implementation 'com.google.android.material:material:1.7.0' implementation 'com.google.android.material:material:1.8.0'
implementation "androidx.recyclerview:recyclerview:$recyclerview_version" implementation "androidx.recyclerview:recyclerview:$recyclerview_version"
implementation "androidx.preference:preference-ktx:$preferences_version" implementation "androidx.preference:preference-ktx:$preferences_version"
implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0" implementation "androidx.swiperefreshlayout:swiperefreshlayout:1.1.0"
//Desugaring //Desugaring
coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.6' coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:2.0.2'
implementation('com.jaredrummler:android-device-names:2.1.0') implementation('com.jaredrummler:android-device-names:2.1.1')
// IAP Handling / Verification // IAP Handling / Verification
implementation "com.android.billingclient:billing-ktx:5.1.0" implementation "com.android.billingclient:billing-ktx:5.1.0"
@ -62,38 +58,38 @@ dependencies {
implementation "com.amplitude:analytics-android:$amplitude_version" implementation "com.amplitude:analytics-android:$amplitude_version"
//Tests //Tests
testImplementation 'io.kotest:kotest-runner-junit5:5.3.0' testImplementation 'io.kotest:kotest-runner-junit5:5.5.5'
testImplementation 'androidx.test:core:1.5.0' testImplementation 'androidx.test:core:1.5.0'
testImplementation 'io.mockk:mockk:1.13.3' testImplementation 'io.mockk:mockk:1.13.4'
testImplementation 'io.mockk:mockk-android:1.13.3' testImplementation 'io.mockk:mockk-android:1.13.4'
testImplementation 'io.kotest:kotest-assertions-core:5.3.0' testImplementation 'io.kotest:kotest-assertions-core:5.5.5'
testImplementation 'io.kotest:kotest-framework-datatest:5.3.0' testImplementation 'io.kotest:kotest-framework-datatest:5.5.5'
androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.4.1') { androidTestImplementation ('com.kaspersky.android-components:kaspresso:1.5.1') {
exclude module: "protobuf-lite" exclude module: "protobuf-lite"
} }
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.0' androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
androidTestImplementation 'androidx.test:runner:1.5.1' androidTestImplementation 'androidx.test:runner:1.5.2'
androidTestImplementation 'androidx.test:rules:1.5.0' androidTestImplementation 'androidx.test:rules:1.5.0'
debugImplementation 'androidx.fragment:fragment-testing:1.5.5' debugImplementation 'androidx.fragment:fragment-testing:1.5.5'
androidTestImplementation 'androidx.test.ext:junit:1.1.4' androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test:core-ktx:1.5.0' androidTestImplementation 'androidx.test:core-ktx:1.5.0'
androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.4' androidTestImplementation 'androidx.test.ext:junit-ktx:1.1.5'
androidTestImplementation 'io.mockk:mockk-android:1.13.3' androidTestImplementation 'io.mockk:mockk-android:1.13.4'
androidTestImplementation 'io.kotest:kotest-assertions-core:5.3.0' androidTestImplementation 'io.kotest:kotest-assertions-core:5.5.5'
androidTestUtil("androidx.test:orchestrator:1.4.2") androidTestUtil("androidx.test:orchestrator:1.4.2")
implementation 'com.facebook.shimmer:shimmer:0.5.0' implementation 'com.facebook.shimmer:shimmer:0.5.0'
//Leak Detection //Leak Detection
debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.9.1' debugImplementation 'com.squareup.leakcanary:leakcanary-android:2.10'
//Push Notifications //Push Notifications
implementation platform("com.google.firebase:firebase-bom:$firebase_bom") implementation platform("com.google.firebase:firebase-bom:$firebase_bom")
implementation 'com.google.firebase:firebase-analytics-ktx'
implementation 'com.google.firebase:firebase-crashlytics-ktx' implementation 'com.google.firebase:firebase-crashlytics-ktx'
implementation 'com.google.firebase:firebase-core'
implementation 'com.google.firebase:firebase-messaging-ktx' implementation 'com.google.firebase:firebase-messaging-ktx'
implementation 'com.google.firebase:firebase-config-ktx' implementation 'com.google.firebase:firebase-config-ktx'
implementation 'com.google.firebase:firebase-perf-ktx' implementation 'com.google.firebase:firebase-perf-ktx'
implementation 'com.google.android.gms:play-services-ads:21.4.0' implementation 'com.google.android.gms:play-services-ads:21.5.0'
implementation "com.google.android.gms:play-services-auth:$play_auth_version" implementation "com.google.android.gms:play-services-auth:$play_auth_version"
implementation 'com.google.android.flexbox:flexbox:3.0.0' implementation 'com.google.android.flexbox:flexbox:3.0.0'
implementation "com.google.android.gms:play-services-wearable:$play_wearables_version" implementation "com.google.android.gms:play-services-wearable:$play_wearables_version"
@ -114,7 +110,7 @@ dependencies {
implementation 'androidx.activity:activity-compose:1.6.1' implementation 'androidx.activity:activity-compose:1.6.1'
implementation "androidx.compose.runtime:runtime-livedata:$compose_version" implementation "androidx.compose.runtime:runtime-livedata:$compose_version"
implementation "androidx.compose.material:material:$compose_version" implementation "androidx.compose.material:material:1.3.1"
implementation "androidx.compose.animation:animation:$compose_version" implementation "androidx.compose.animation:animation:$compose_version"
implementation "androidx.compose.ui:ui-tooling:$compose_version" implementation "androidx.compose.ui:ui-tooling:$compose_version"
implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version" implementation "androidx.lifecycle:lifecycle-viewmodel-compose:$lifecycle_version"
@ -124,18 +120,12 @@ dependencies {
implementation project(':common') implementation project(':common')
implementation project(':shared') implementation project(':shared')
ktlint('com.pinterest:ktlint:0.45.2') {
attributes {
attribute(Bundling.BUNDLING_ATTRIBUTE, getObjects().named(Bundling, Bundling.EXTERNAL))
}
}
androidTestImplementation "org.jetbrains.kotlin:kotlin-reflect:$kotlin_version"
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
} }
android { android {
compileSdkVersion target_sdk compileSdkVersion target_sdk
buildToolsVersion '30.0.3' buildToolsVersion '33.0.2'
testOptions { testOptions {
unitTests { unitTests {
includeAndroidResources = true includeAndroidResources = true
@ -169,7 +159,7 @@ android {
} }
composeOptions { composeOptions {
kotlinCompilerExtensionVersion = "1.4.0-alpha02" kotlinCompilerExtensionVersion = "1.4.2"
} }
signingConfigs { signingConfigs {
@ -387,24 +377,4 @@ gradle.projectsEvaluated {
} }
} }
apply plugin: 'com.google.gms.google-services' apply plugin: 'com.google.gms.google-services'
task ktlint(type: JavaExec, group: "verification") {
description = "Check Kotlin code style."
classpath = configurations.ktlint
mainClass.set("com.pinterest.ktlint.Main")
args "--disabled_rules=max-line-length", "--android", "--reporter=plain?group_by_file", "src/**/*.kt"
// to generate report in checkstyle format prepend following args:
// "--reporter=plain", "--reporter=checkstyle,output=${buildDir}/ktlint.xml"
// to add a baseline to check against prepend following args:
// "--baseline=ktlint-baseline.xml"
// see https://github.com/pinterest/ktlint#usage for more
}
check.dependsOn ktlint
task ktlintFormat(type: JavaExec, group: "formatting") {
description = "Fix Kotlin code style deviations."
classpath = configurations.ktlint
mainClass.set("com.pinterest.ktlint.Main")
args "--disabled_rules=max-lint-length", "--android", "-F", "src/**/*.kt"
}

View file

@ -11,8 +11,8 @@ import androidx.test.filters.LargeTest
import com.habitrpg.android.habitica.HabiticaBaseApplication import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.shared.habitica.models.tasks.Frequency
import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.shared.habitica.models.tasks.Frequency
import com.habitrpg.shared.habitica.models.tasks.TaskType import com.habitrpg.shared.habitica.models.tasks.TaskType
import io.github.kakaocup.kakao.common.assertions.BaseAssertions import io.github.kakaocup.kakao.common.assertions.BaseAssertions
import io.github.kakaocup.kakao.common.matchers.ChildCountMatcher import io.github.kakaocup.kakao.common.matchers.ChildCountMatcher
@ -31,12 +31,12 @@ import io.mockk.mockkObject
import io.mockk.slot import io.mockk.slot
import io.mockk.verify import io.mockk.verify
import io.reactivex.rxjava3.core.Flowable import io.reactivex.rxjava3.core.Flowable
import java.util.Date
import java.util.UUID
import org.junit.After import org.junit.After
import org.junit.Before import org.junit.Before
import org.junit.Test import org.junit.Test
import org.junit.runner.RunWith import org.junit.runner.RunWith
import java.util.Date
import java.util.UUID
class TaskFormScreen : Screen<TaskFormScreen>() { class TaskFormScreen : Screen<TaskFormScreen>() {
val toolbar = KToolbar { withId(R.id.toolbar) } val toolbar = KToolbar { withId(R.id.toolbar) }

View file

@ -26,9 +26,9 @@ class NavigationDrawerScreen : Screen<NavigationDrawerScreen>() {
val recycler: KRecyclerView = KRecyclerView({ val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView) withId(R.id.recyclerView)
}, itemTypeBuilder = { }, itemTypeBuilder = {
itemType(::SectionHeaderItem) itemType(::SectionHeaderItem)
itemType(::MainItem) itemType(::MainItem)
}) })
} }
@LargeTest @LargeTest

View file

@ -63,8 +63,8 @@ class ItemScreen : Screen<ItemScreen>() {
val recycler: KRecyclerView = KRecyclerView({ val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView) withId(R.id.recyclerView)
}, itemTypeBuilder = { }, itemTypeBuilder = {
itemType(::ItemItem) itemType(::ItemItem)
}) })
} }
internal class ItemRecyclerFragmentTest : FragmentTestCase<ItemRecyclerFragment, FragmentRecyclerviewBinding, ItemScreen>(false) { internal class ItemRecyclerFragmentTest : FragmentTestCase<ItemRecyclerFragment, FragmentRecyclerviewBinding, ItemScreen>(false) {

View file

@ -21,7 +21,6 @@ import io.mockk.every
import io.mockk.mockk import io.mockk.mockk
import io.mockk.mockkObject import io.mockk.mockkObject
import io.mockk.spyk import io.mockk.spyk
import io.mockk.verify
import kotlinx.coroutines.flow.flowOf import kotlinx.coroutines.flow.flowOf
import org.junit.Test import org.junit.Test
@ -29,9 +28,9 @@ class PetDetailScreen : Screen<PetDetailScreen>() {
val recycler: KRecyclerView = KRecyclerView({ val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView) withId(R.id.recyclerView)
}, itemTypeBuilder = { }, itemTypeBuilder = {
itemType(::SectionItem) itemType(::SectionItem)
itemType(::PetItem) itemType(::PetItem)
}) })
} }
internal class PetDetailRecyclerFragmentTest : internal class PetDetailRecyclerFragmentTest :

View file

@ -33,9 +33,9 @@ class StableScreen : Screen<StableScreen>() {
val recycler: KRecyclerView = KRecyclerView({ val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView) withId(R.id.recyclerView)
}, itemTypeBuilder = { }, itemTypeBuilder = {
itemType(::SectionItem) itemType(::SectionItem)
itemType(::PetItem) itemType(::PetItem)
}) })
} }
internal class StableRecyclerFragmentTest : FragmentTestCase<StableRecyclerFragment, FragmentRecyclerviewBinding, StableScreen>(false) { internal class StableRecyclerFragmentTest : FragmentTestCase<StableRecyclerFragment, FragmentRecyclerviewBinding, StableScreen>(false) {

View file

@ -7,8 +7,8 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
import com.habitrpg.android.habitica.models.tasks.Task import com.habitrpg.android.habitica.models.tasks.Task
import com.habitrpg.android.habitica.models.tasks.TaskList import com.habitrpg.android.habitica.models.tasks.TaskList
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.android.habitica.ui.fragments.FragmentTestCase import com.habitrpg.android.habitica.ui.fragments.FragmentTestCase
import com.habitrpg.shared.habitica.models.tasks.TaskType
import io.github.kakaocup.kakao.common.views.KView import io.github.kakaocup.kakao.common.views.KView
import io.github.kakaocup.kakao.recycler.KRecyclerItem import io.github.kakaocup.kakao.recycler.KRecyclerItem
import io.github.kakaocup.kakao.recycler.KRecyclerView import io.github.kakaocup.kakao.recycler.KRecyclerView
@ -30,8 +30,8 @@ class TaskListScreen : Screen<TaskListScreen>() {
val recycler: KRecyclerView = KRecyclerView({ val recycler: KRecyclerView = KRecyclerView({
withId(R.id.recyclerView) withId(R.id.recyclerView)
}, itemTypeBuilder = { }, itemTypeBuilder = {
itemType(::TaskItem) itemType(::TaskItem)
}) })
} }
internal class TaskRecyclerViewFragmentTest : FragmentTestCase<TaskRecyclerViewFragment, FragmentRefreshRecyclerviewBinding, TaskListScreen>(false) { internal class TaskRecyclerViewFragmentTest : FragmentTestCase<TaskRecyclerViewFragment, FragmentRefreshRecyclerviewBinding, TaskListScreen>(false) {

View file

@ -81,7 +81,6 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
AppCompatDelegate.setCompatVectorFromResourcesEnabled(true) AppCompatDelegate.setCompatVectorFromResourcesEnabled(true)
setupCoil() setupCoil()
ExceptionHandler.init(analyticsManager) ExceptionHandler.init(analyticsManager)

View file

@ -461,5 +461,5 @@ interface ApiService {
suspend fun getHallMember(@Path("memberID") memberID: String): HabitResponse<Member> suspend fun getHallMember(@Path("memberID") memberID: String): HabitResponse<Member>
@POST("tasks/{taskID}/needs-work/{userID}") @POST("tasks/{taskID}/needs-work/{userID}")
suspend fun markTaskNeedsWork(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse<Task> suspend fun markTaskNeedsWork(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse<Task>
} }

View file

@ -9,4 +9,4 @@ interface MaintenanceApiService {
@GET("deprecation-android.json") @GET("deprecation-android.json")
suspend fun getDepricationStatus(): MaintenanceResponse? suspend fun getDepricationStatus(): MaintenanceResponse?
} }

View file

@ -4,7 +4,7 @@ import com.habitrpg.android.habitica.models.ContentResult
import com.habitrpg.android.habitica.models.WorldState import com.habitrpg.android.habitica.models.WorldState
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
interface ContentRepository: BaseRepository { interface ContentRepository : BaseRepository {
suspend fun retrieveContent(forced: Boolean = false): ContentResult? suspend fun retrieveContent(forced: Boolean = false): ContentResult?
suspend fun retrieveWorldState(forced: Boolean = false): WorldState? suspend fun retrieveWorldState(forced: Boolean = false): WorldState?

View file

@ -68,5 +68,4 @@ interface TaskRepository : BaseRepository {
fun getTasksForChallenge(challengeID: String?): Flow<List<Task>> fun getTasksForChallenge(challengeID: String?): Flow<List<Task>>
suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData? suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData?
suspend fun markTaskNeedsWork(task: Task, userID: String) suspend fun markTaskNeedsWork(task: Task, userID: String)
} }

View file

@ -74,7 +74,7 @@ class ApiClientImpl(
private val analyticsManager: AnalyticsManager, private val analyticsManager: AnalyticsManager,
private val notificationsManager: NotificationsManager, private val notificationsManager: NotificationsManager,
private val context: Context private val context: Context
): ApiClient { ) : ApiClient {
private lateinit var retrofitAdapter: Retrofit private lateinit var retrofitAdapter: Retrofit
@ -325,7 +325,7 @@ class ApiClientImpl(
override suspend fun getStatus(): Status? = process { apiService.getStatus() } override suspend fun getStatus(): Status? = process { apiService.getStatus() }
override suspend fun getContent(language: String?): ContentResult? { override suspend fun getContent(language: String?): ContentResult? {
return process { apiService.getContent(language ?: this.languageCode) } return process { apiService.getContent(language ?: this.languageCode) }
} }
override suspend fun updateUser(updateDictionary: Map<String, Any>): User? { override suspend fun updateUser(updateDictionary: Map<String, Any>): User? {
@ -479,11 +479,11 @@ class ApiClientImpl(
override suspend fun revive(): User? = process { apiService.revive() } override suspend fun revive(): User? = process { apiService.revive() }
suspend override fun useSkill(skillName: String, targetType: String, targetId: String): SkillResponse? { override suspend fun useSkill(skillName: String, targetType: String, targetId: String): SkillResponse? {
return process { apiService.useSkill(skillName, targetType, targetId) } return process { apiService.useSkill(skillName, targetType, targetId) }
} }
suspend override fun useSkill(skillName: String, targetType: String): SkillResponse? { override suspend fun useSkill(skillName: String, targetType: String): SkillResponse? {
return process { apiService.useSkill(skillName, targetType) } return process { apiService.useSkill(skillName, targetType) }
} }

View file

@ -75,7 +75,7 @@ class ChallengeRepositoryImpl(
return tasksOrder return tasksOrder
} }
private suspend fun addChallengeTasks(challenge: Challenge, addedTaskList: List<Task>) { private suspend fun addChallengeTasks(challenge: Challenge, addedTaskList: List<Task>) {
when { when {
addedTaskList.count() == 1 -> apiClient.createChallengeTask(challenge.id ?: "", addedTaskList[0]) addedTaskList.count() == 1 -> apiClient.createChallengeTask(challenge.id ?: "", addedTaskList[0])
addedTaskList.count() > 1 -> apiClient.createChallengeTasks(challenge.id ?: "", addedTaskList) addedTaskList.count() > 1 -> apiClient.createChallengeTasks(challenge.id ?: "", addedTaskList)

View file

@ -48,7 +48,7 @@ class InventoryRepositoryImpl(
} }
override suspend fun retrieveInAppRewards(): List<ShopItem>? { override suspend fun retrieveInAppRewards(): List<ShopItem>? {
val rewards = apiClient.retrieveInAppRewards() val rewards = apiClient.retrieveInAppRewards()
if (rewards != null) { if (rewards != null) {
localRepository.saveInAppRewards(rewards) localRepository.saveInAppRewards(rewards)
} }

View file

@ -306,17 +306,17 @@ class SocialRepositoryImpl(
override suspend fun acceptQuest(user: User?, partyId: String): Void? { override suspend fun acceptQuest(user: User?, partyId: String): Void? {
apiClient.acceptQuest(partyId) apiClient.acceptQuest(partyId)
user?.let { user?.let {
localRepository.updateRSVPNeeded(it, false) localRepository.updateRSVPNeeded(it, false)
} }
return null return null
} }
override suspend fun rejectQuest(user: User?, partyId: String): Void? { override suspend fun rejectQuest(user: User?, partyId: String): Void? {
apiClient.rejectQuest(partyId) apiClient.rejectQuest(partyId)
user?.let { user?.let {
localRepository.updateRSVPNeeded(it, false) localRepository.updateRSVPNeeded(it, false)
} }
return null return null
} }

View file

@ -43,7 +43,6 @@ class UserRepositoryImpl(
private var lastSync: Date? = null private var lastSync: Date? = null
} }
override fun getUser(): Flow<User?> = getUser(userID) override fun getUser(): Flow<User?> = getUser(userID)
override fun getUser(userID: String): Flow<User?> = localRepository.getUser(userID) override fun getUser(userID: String): Flow<User?> = localRepository.getUser(userID)
@ -126,7 +125,7 @@ class UserRepositoryImpl(
val response = apiClient.useSkill(key, target ?: "", taskId) ?: return null val response = apiClient.useSkill(key, target ?: "", taskId) ?: return null
val user = getLiveUser() ?: return response val user = getLiveUser() ?: return response
response.hpDiff = (response.user?.stats?.hp ?: 0.0) - (user.stats?.hp ?: 0.0) response.hpDiff = (response.user?.stats?.hp ?: 0.0) - (user.stats?.hp ?: 0.0)
response.expDiff =(response.user?.stats?.exp ?: 0.0) - (user.stats?.exp ?: 0.0) response.expDiff = (response.user?.stats?.exp ?: 0.0) - (user.stats?.exp ?: 0.0)
response.goldDiff = (response.user?.stats?.gp ?: 0.0) - (user.stats?.gp ?: 0.0) response.goldDiff = (response.user?.stats?.gp ?: 0.0) - (user.stats?.gp ?: 0.0)
response.damage = (response.user?.party?.quest?.progress?.up ?: 0.0f) - (user.party?.quest?.progress?.up ?: 0.0f) response.damage = (response.user?.party?.quest?.progress?.up ?: 0.0f) - (user.party?.quest?.progress?.up ?: 0.0f)
response.user?.let { mergeUser(user, it) } response.user?.let { mergeUser(user, it) }
@ -137,7 +136,7 @@ class UserRepositoryImpl(
val response = apiClient.useSkill(key, target ?: "") ?: return null val response = apiClient.useSkill(key, target ?: "") ?: return null
val user = getLiveUser() ?: return response val user = getLiveUser() ?: return response
response.hpDiff = (response.user?.stats?.hp ?: 0.0) - (user.stats?.hp ?: 0.0) response.hpDiff = (response.user?.stats?.hp ?: 0.0) - (user.stats?.hp ?: 0.0)
response.expDiff =(response.user?.stats?.exp ?: 0.0) - (user.stats?.exp ?: 0.0) response.expDiff = (response.user?.stats?.exp ?: 0.0) - (user.stats?.exp ?: 0.0)
response.goldDiff = (response.user?.stats?.gp ?: 0.0) - (user.stats?.gp ?: 0.0) response.goldDiff = (response.user?.stats?.gp ?: 0.0) - (user.stats?.gp ?: 0.0)
response.damage = (response.user?.party?.quest?.progress?.up ?: 0.0f) - (user.party?.quest?.progress?.up ?: 0.0f) response.damage = (response.user?.party?.quest?.progress?.up ?: 0.0f) - (user.party?.quest?.progress?.up ?: 0.0f)
response.user?.let { mergeUser(user, it) } response.user?.let { mergeUser(user, it) }

View file

@ -65,5 +65,4 @@ interface InventoryLocalRepository : ContentLocalRepository {
fun getLiveObject(obj: OwnedItem): OwnedItem? fun getLiveObject(obj: OwnedItem): OwnedItem?
fun getItems(itemClass: Class<out Item>): Flow<List<Item>> fun getItems(itemClass: Class<out Item>): Flow<List<Item>>
fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>> fun getItems(itemClass: Class<out Item>, keys: Array<String>): Flow<List<Item>>
} }

View file

@ -48,7 +48,7 @@ abstract class RealmBaseLocalRepository internal constructor(override var realm:
private var pendingSaves = mutableListOf<Any>() private var pendingSaves = mutableListOf<Any>()
} }
private fun <T: RealmModel> copy(realm: Realm, obj: T) { private fun <T : RealmModel> copy(realm: Realm, obj: T) {
try { try {
realm.insertOrUpdate(obj) realm.insertOrUpdate(obj)
} catch (_: java.lang.IllegalArgumentException) { } catch (_: java.lang.IllegalArgumentException) {
@ -119,9 +119,9 @@ abstract class RealmBaseLocalRepository internal constructor(override var realm:
fun queryUser(userID: String): Flow<User?> { fun queryUser(userID: String): Flow<User?> {
return realm.where(User::class.java) return realm.where(User::class.java)
.equalTo("id", userID) .equalTo("id", userID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded && it.isValid && !it.isEmpty() } .filter { it.isLoaded && it.isValid && !it.isEmpty() }
.map { it.firstOrNull() } .map { it.firstOrNull() }
} }

View file

@ -18,61 +18,61 @@ import kotlinx.coroutines.flow.map
class RealmChallengeLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), ChallengeLocalRepository { class RealmChallengeLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), ChallengeLocalRepository {
override fun isChallengeMember(userID: String, challengeID: String): Flow<Boolean> = realm.where(ChallengeMembership::class.java) override fun isChallengeMember(userID: String, challengeID: String): Flow<Boolean> = realm.where(ChallengeMembership::class.java)
.equalTo("userID", userID) .equalTo("userID", userID)
.equalTo("challengeID", challengeID) .equalTo("challengeID", challengeID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
.map { it.count() > 0 } .map { it.count() > 0 }
override fun getChallengeMembership(userId: String, id: String) = realm.where(ChallengeMembership::class.java) override fun getChallengeMembership(userId: String, id: String) = realm.where(ChallengeMembership::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.equalTo("challengeID", id) .equalTo("challengeID", id)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
.map { it.first() } .map { it.first() }
.filterNotNull() .filterNotNull()
override fun getChallengeMemberships(userId: String) = realm.where(ChallengeMembership::class.java) override fun getChallengeMemberships(userId: String) = realm.where(ChallengeMembership::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
override fun getChallenge(id: String): Flow<Challenge> { override fun getChallenge(id: String): Flow<Challenge> {
return realm.where(Challenge::class.java) return realm.where(Challenge::class.java)
.equalTo("id", id) .equalTo("id", id)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() } .filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() }
.map { it.first() } .map { it.first() }
.filterNotNull() .filterNotNull()
} }
override fun getTasks(challengeID: String): Flow<List<Task>> { override fun getTasks(challengeID: String): Flow<List<Task>> {
return realm.where(Task::class.java) return realm.where(Task::class.java)
.equalTo("userId", challengeID) .equalTo("userId", challengeID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { realmObject -> realmObject.isLoaded } .filter { realmObject -> realmObject.isLoaded }
} }
override val challenges: Flow<List<Challenge>> override val challenges: Flow<List<Challenge>>
get() = realm.where(Challenge::class.java) get() = realm.where(Challenge::class.java)
.isNotNull("name") .isNotNull("name")
.sort("official", Sort.DESCENDING, "createdAt", Sort.DESCENDING) .sort("official", Sort.DESCENDING, "createdAt", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override fun getUserChallenges(userId: String): Flow<List<Challenge>> { override fun getUserChallenges(userId: String): Flow<List<Challenge>> {
return realm.where(ChallengeMembership::class.java) return realm.where(ChallengeMembership::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
.flatMapLatest { it -> .flatMapLatest { it ->
val ids = it.map { val ids = it.map {
return@map it.challengeID return@map it.challengeID

View file

@ -39,10 +39,10 @@ open class RealmContentLocalRepository(realm: Realm) : RealmBaseLocalRepository(
override fun getWorldState(): Flow<WorldState> { override fun getWorldState(): Flow<WorldState> {
return realm.where(WorldState::class.java) return realm.where(WorldState::class.java)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded && it.size > 0 } .filter { it.isLoaded && it.size > 0 }
.map { it.first() } .map { it.first() }
.filterNotNull() .filterNotNull()
} }

View file

@ -30,9 +30,9 @@ class RealmCustomizationLocalRepository(realm: Realm) : RealmContentLocalReposit
.endGroup() .endGroup()
} }
return query return query
.sort("customizationSet") .sort("customizationSet")
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
} }

View file

@ -12,17 +12,17 @@ import kotlinx.coroutines.flow.map
class RealmFAQLocalRepository(realm: Realm) : RealmContentLocalRepository(realm), FAQLocalRepository { class RealmFAQLocalRepository(realm: Realm) : RealmContentLocalRepository(realm), FAQLocalRepository {
override fun getArticle(position: Int): Flow<FAQArticle> { override fun getArticle(position: Int): Flow<FAQArticle> {
return realm.where(FAQArticle::class.java) return realm.where(FAQArticle::class.java)
.equalTo("position", position) .equalTo("position", position)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded && it.count() > 0 } .filter { it.isLoaded && it.count() > 0 }
.map { it.firstOrNull() } .map { it.firstOrNull() }
.filterNotNull() .filterNotNull()
} }
override val articles: Flow<List<FAQArticle>> override val articles: Flow<List<FAQArticle>>
get() = realm.where(FAQArticle::class.java) get() = realm.where(FAQArticle::class.java)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }

View file

@ -30,7 +30,8 @@ import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale
class RealmInventoryLocalRepository(realm: Realm) : RealmContentLocalRepository(realm), class RealmInventoryLocalRepository(realm: Realm) :
RealmContentLocalRepository(realm),
InventoryLocalRepository { InventoryLocalRepository {
override fun getQuestContent(keys: List<String>): Flow<List<QuestContent>> { override fun getQuestContent(keys: List<String>): Flow<List<QuestContent>> {
return realm.where(QuestContent::class.java) return realm.where(QuestContent::class.java)

View file

@ -21,18 +21,18 @@ import kotlinx.coroutines.flow.map
class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), SocialLocalRepository { class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), SocialLocalRepository {
override fun getGroupMembership(userId: String, id: String) = realm.where(GroupMembership::class.java) override fun getGroupMembership(userId: String, id: String) = realm.where(GroupMembership::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.equalTo("groupID", id) .equalTo("groupID", id)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded && it.isNotEmpty() } .filter { it.isLoaded && it.isNotEmpty() }
.map { it.first() } .map { it.first() }
override fun getGroupMemberships(userId: String): Flow<List<GroupMembership>> = realm.where(GroupMembership::class.java) override fun getGroupMemberships(userId: String): Flow<List<GroupMembership>> = realm.where(GroupMembership::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
override fun updateMembership(userId: String, id: String, isMember: Boolean) { override fun updateMembership(userId: String, id: String, isMember: Boolean) {
if (isMember) { if (isMember) {
@ -129,19 +129,19 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
} }
override fun getPublicGuilds() = realm.where(Group::class.java) override fun getPublicGuilds() = realm.where(Group::class.java)
.equalTo("type", "guild") .equalTo("type", "guild")
.equalTo("privacy", "public") .equalTo("privacy", "public")
.notEqualTo("id", Group.TAVERN_ID) .notEqualTo("id", Group.TAVERN_ID)
.sort("memberCount", Sort.DESCENDING) .sort("memberCount", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override fun getUserGroups(userID: String, type: String?) = realm.where(GroupMembership::class.java) override fun getUserGroups(userID: String, type: String?) = realm.where(GroupMembership::class.java)
.equalTo("userID", userID) .equalTo("userID", userID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
.flatMapLatest { memberships -> .flatMapLatest { memberships ->
realm.where(Group::class.java) realm.where(Group::class.java)
@ -160,10 +160,10 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
override fun getGroups(type: String): Flow<List<Group>> { override fun getGroups(type: String): Flow<List<Group>> {
return realm.where(Group::class.java) return realm.where(Group::class.java)
.equalTo("type", type) .equalTo("type", type)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
override fun getGroup(id: String): Flow<Group?> { override fun getGroup(id: String): Flow<Group?> {
@ -177,11 +177,11 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
override fun getGroupChat(groupId: String): Flow<List<ChatMessage>> { override fun getGroupChat(groupId: String): Flow<List<ChatMessage>> {
return realm.where(ChatMessage::class.java) return realm.where(ChatMessage::class.java)
.equalTo("groupId", groupId) .equalTo("groupId", groupId)
.sort("timestamp", Sort.DESCENDING) .sort("timestamp", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
override fun deleteMessage(id: String) { override fun deleteMessage(id: String) {
@ -190,9 +190,9 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
} }
override fun getPartyMembers(partyId: String) = realm.where(Member::class.java) override fun getPartyMembers(partyId: String) = realm.where(Member::class.java)
.equalTo("party.id", partyId) .equalTo("party.id", partyId)
.findAll() .findAll()
.toFlow() .toFlow()
override fun getGroupMembers(groupID: String) = realm.where(GroupMembership::class.java) override fun getGroupMembers(groupID: String) = realm.where(GroupMembership::class.java)
.equalTo("groupID", groupID) .equalTo("groupID", groupID)
@ -291,18 +291,18 @@ class RealmSocialLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm)
} }
override fun getInboxMessages(userId: String, replyToUserID: String?) = realm.where(ChatMessage::class.java) override fun getInboxMessages(userId: String, replyToUserID: String?) = realm.where(ChatMessage::class.java)
.equalTo("isInboxMessage", true) .equalTo("isInboxMessage", true)
.equalTo("uuid", replyToUserID) .equalTo("uuid", replyToUserID)
.equalTo("userID", userId) .equalTo("userID", userId)
.sort("timestamp", Sort.DESCENDING) .sort("timestamp", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
override fun getInboxConversation(userId: String) = realm.where(InboxConversation::class.java) override fun getInboxConversation(userId: String) = realm.where(InboxConversation::class.java)
.equalTo("userID", userId) .equalTo("userID", userId)
.sort("timestamp", Sort.DESCENDING) .sort("timestamp", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }

View file

@ -56,10 +56,10 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getTasks(userId: String): Flow<List<Task>> { override fun getTasks(userId: String): Flow<List<Task>> {
if (realm.isClosed) return emptyFlow() if (realm.isClosed) return emptyFlow()
return realm.where(Task::class.java).equalTo("userId", userId) return realm.where(Task::class.java).equalTo("userId", userId)
.sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING) .sort("position", Sort.ASCENDING, "dateCreated", Sort.DESCENDING)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
override fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList) { override fun saveTasks(ownerID: String, tasksOrder: TasksOrder, tasks: TaskList) {
@ -218,10 +218,10 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getTaskAtPosition(taskType: String, position: Int): Flow<Task> { override fun getTaskAtPosition(taskType: String, position: Int): Flow<Task> {
return realm.where(Task::class.java).equalTo("typeValue", taskType).equalTo("position", position) return realm.where(Task::class.java).equalTo("typeValue", taskType).equalTo("position", position)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() } .filter { realmObject -> realmObject.isLoaded && realmObject.isNotEmpty() }
.map { it.first() } .map { it.first() }
.filterNotNull() .filterNotNull()
} }
@ -244,12 +244,12 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getErroredTasks(userID: String): Flow<List<Task>> { override fun getErroredTasks(userID: String): Flow<List<Task>> {
return realm.where(Task::class.java) return realm.where(Task::class.java)
.equalTo("userId", userID) .equalTo("userId", userID)
.equalTo("hasErrored", true) .equalTo("hasErrored", true)
.sort("position") .sort("position")
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
override fun getUser(userID: String): Flow<User> { override fun getUser(userID: String): Flow<User> {
@ -264,10 +264,10 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getTasksForChallenge(challengeID: String?, userID: String?): Flow<List<Task>> { override fun getTasksForChallenge(challengeID: String?, userID: String?): Flow<List<Task>> {
return realm.where(Task::class.java) return realm.where(Task::class.java)
.equalTo("challengeID", challengeID) .equalTo("challengeID", challengeID)
.equalTo("userId", userID) .equalTo("userId", userID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
} }

View file

@ -15,19 +15,19 @@ class RealmTutorialLocalRepository(realm: Realm) : RealmBaseLocalRepository(real
override fun getTutorialStep(key: String): Flow<TutorialStep> { override fun getTutorialStep(key: String): Flow<TutorialStep> {
if (realm.isClosed) return emptyFlow() if (realm.isClosed) return emptyFlow()
return realm.where(TutorialStep::class.java).equalTo("identifier", key) return realm.where(TutorialStep::class.java).equalTo("identifier", key)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { realmObject -> realmObject.isLoaded && realmObject.isValid && realmObject.isNotEmpty() } .filter { realmObject -> realmObject.isLoaded && realmObject.isValid && realmObject.isNotEmpty() }
.map { steps -> steps.first() } .map { steps -> steps.first() }
.filterNotNull() .filterNotNull()
} }
override fun getTutorialSteps(keys: List<String>): Flow<out List<TutorialStep>> { override fun getTutorialSteps(keys: List<String>): Flow<out List<TutorialStep>> {
if (realm.isClosed) return emptyFlow() if (realm.isClosed) return emptyFlow()
return realm.where(TutorialStep::class.java) return realm.where(TutorialStep::class.java)
.`in`("identifier", keys.toTypedArray()) .`in`("identifier", keys.toTypedArray())
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
} }

View file

@ -21,7 +21,8 @@ import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
import kotlinx.coroutines.flow.map import kotlinx.coroutines.flow.map
class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm), class RealmUserLocalRepository(realm: Realm) :
RealmBaseLocalRepository(realm),
UserLocalRepository { UserLocalRepository {
@OptIn(ExperimentalCoroutinesApi::class) @OptIn(ExperimentalCoroutinesApi::class)
override fun getUserQuestStatus(userID: String): Flow<UserQuestStatus> { override fun getUserQuestStatus(userID: String): Flow<UserQuestStatus> {
@ -30,13 +31,13 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
.map { it.party?.id ?: "" } .map { it.party?.id ?: "" }
.filter { it.isNotBlank() } .filter { it.isNotBlank() }
.flatMapLatest { .flatMapLatest {
realm.where(Group::class.java) realm.where(Group::class.java)
.equalTo("id", it) .equalTo("id", it)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { groups -> groups.size > 0 } .filter { groups -> groups.size > 0 }
.map { it.firstOrNull() } .map { it.firstOrNull() }
.filterNotNull() .filterNotNull()
} }
.map { .map {
when { when {
@ -127,22 +128,22 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
override fun getTeamPlan(teamID: String): Flow<Group?> { override fun getTeamPlan(teamID: String): Flow<Group?> {
if (realm.isClosed) return emptyFlow() if (realm.isClosed) return emptyFlow()
return realm.where(Group::class.java) return realm.where(Group::class.java)
.equalTo("id", teamID) .equalTo("id", teamID)
.findAll() .findAll()
.toFlow() .toFlow()
.filter { realmObject -> realmObject.isLoaded && realmObject.isValid } .filter { realmObject -> realmObject.isLoaded && realmObject.isValid }
.map { teams -> teams.firstOrNull() } .map { teams -> teams.firstOrNull() }
} }
override fun getSkills(user: User): Flow<List<Skill>> { override fun getSkills(user: User): Flow<List<Skill>> {
val habitClass = val habitClass =
if (user.preferences?.disableClasses == true) "none" else user.stats?.habitClass if (user.preferences?.disableClasses == true) "none" else user.stats?.habitClass
return realm.where(Skill::class.java) return realm.where(Skill::class.java)
.equalTo("habitClass", habitClass) .equalTo("habitClass", habitClass)
.sort("lvl") .sort("lvl")
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
override fun getSpecialItems(user: User): Flow<List<Skill>> { override fun getSpecialItems(user: User): Flow<List<Skill>> {
@ -154,9 +155,9 @@ class RealmUserLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
} }
} }
return realm.where(Skill::class.java) return realm.where(Skill::class.java)
.`in`("key", ownedItems.toTypedArray()) .`in`("key", ownedItems.toTypedArray())
.findAll() .findAll()
.toFlow() .toFlow()
.filter { it.isLoaded } .filter { it.isLoaded }
} }
} }

View file

@ -16,7 +16,7 @@ import com.habitrpg.common.habitica.helpers.AppTestingLevel
import kotlinx.coroutines.MainScope import kotlinx.coroutines.MainScope
import java.util.Date import java.util.Date
class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.common.habitica.helpers.AppConfigManager() { class AppConfigManager(contentRepository: ContentRepository?) : com.habitrpg.common.habitica.helpers.AppConfigManager() {
private var worldState: WorldState? = null private var worldState: WorldState? = null
@ -24,8 +24,8 @@ class AppConfigManager(contentRepository: ContentRepository?): com.habitrpg.comm
try { try {
MainScope().launchCatching { MainScope().launchCatching {
contentRepository?.getWorldState()?.collect { contentRepository?.getWorldState()?.collect {
worldState = it worldState = it
} }
} }
} catch (_: java.lang.IllegalStateException) { } catch (_: java.lang.IllegalStateException) {
// pass // pass

View file

@ -7,13 +7,8 @@ import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineExceptionHandler import kotlinx.coroutines.CoroutineExceptionHandler
import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.internal.http2.ConnectionShutdownException
import okhttp3.internal.http2.StreamResetException
import retrofit2.HttpException import retrofit2.HttpException
import java.io.EOFException
import java.io.IOException import java.io.IOException
import java.net.SocketTimeoutException
import java.net.UnknownHostException
class ExceptionHandler { class ExceptionHandler {
private var analyticsManager: AnalyticsManager? = null private var analyticsManager: AnalyticsManager? = null
@ -52,7 +47,12 @@ class ExceptionHandler {
} }
fun CoroutineScope.launchCatching(errorHandler: ((Throwable) -> Unit)? = null, function: suspend CoroutineScope.() -> Unit) { fun CoroutineScope.launchCatching(errorHandler: ((Throwable) -> Unit)? = null, function: suspend CoroutineScope.() -> Unit) {
launch((ExceptionHandler.coroutine { launch(
errorHandler?.invoke(it) (
}), block = function) ExceptionHandler.coroutine {
} errorHandler?.invoke(it)
}
),
block = function
)
}

View file

@ -20,7 +20,7 @@ object MainNavigationController {
get() { return controllerReference?.get() } get() { return controllerReference?.get() }
val isReady: Boolean val isReady: Boolean
get() = controllerReference?.get() != null get() = controllerReference?.get() != null
fun setup(navController: NavController) { fun setup(navController: NavController) {
this.controllerReference = WeakReference(navController) this.controllerReference = WeakReference(navController)

View file

@ -24,31 +24,31 @@ interface NotificationsManager {
fun dismissTaskNotification(context: Context, task: Task) fun dismissTaskNotification(context: Context, task: Task)
} }
class MainNotificationsManager: NotificationsManager { class MainNotificationsManager : NotificationsManager {
private val seenNotifications: MutableMap<String, Boolean> private val seenNotifications: MutableMap<String, Boolean>
override var apiClient: WeakReference<ApiClient>? = null override var apiClient: WeakReference<ApiClient>? = null
private var lastNotificationHandling: Date? = null private var lastNotificationHandling: Date? = null
val _notifications = MutableStateFlow<List<Notification>?>(null) private val notificationsFlow = MutableStateFlow<List<Notification>?>(null)
val _displaynotificationEvents = Channel<Notification>() private val displayedNotificationEvents = Channel<Notification>()
override val displayNotificationEvents: Flow<Notification> = _displaynotificationEvents.receiveAsFlow().filterNotNull() override val displayNotificationEvents: Flow<Notification> = displayedNotificationEvents.receiveAsFlow().filterNotNull()
init { init {
this.seenNotifications = HashMap() this.seenNotifications = HashMap()
} }
override fun setNotifications(current: List<Notification>) { override fun setNotifications(current: List<Notification>) {
_notifications.value = current notificationsFlow.value = current
this.handlePopupNotifications(current) this.handlePopupNotifications(current)
} }
override fun getNotifications(): Flow<List<Notification>> { override fun getNotifications(): Flow<List<Notification>> {
return _notifications.filterNotNull() return notificationsFlow.filterNotNull()
} }
override fun getNotification(id: String): Notification? { override fun getNotification(id: String): Notification? {
return _notifications.value?.find { it.id == id } return notificationsFlow.value?.find { it.id == id }
} }
override fun dismissTaskNotification(context: Context, task: Task) { override fun dismissTaskNotification(context: Context, task: Task) {
@ -92,7 +92,6 @@ class MainNotificationsManager: NotificationsManager {
Notification.Type.ACHIEVEMENT_FRESHWATER_FRIENDS.type -> true Notification.Type.ACHIEVEMENT_FRESHWATER_FRIENDS.type -> true
Notification.Type.ACHIEVEMENT_GOOD_AS_GOLD.type -> true Notification.Type.ACHIEVEMENT_GOOD_AS_GOLD.type -> true
Notification.Type.ACHIEVEMENT_ALL_THAT_GLITTERS.type -> true Notification.Type.ACHIEVEMENT_ALL_THAT_GLITTERS.type -> true
Notification.Type.ACHIEVEMENT_GOOD_AS_GOLD.type -> true
Notification.Type.ACHIEVEMENT_BONE_COLLECTOR.type -> true Notification.Type.ACHIEVEMENT_BONE_COLLECTOR.type -> true
Notification.Type.ACHIEVEMENT_SKELETON_CREW.type -> true Notification.Type.ACHIEVEMENT_SKELETON_CREW.type -> true
Notification.Type.ACHIEVEMENT_SEEING_RED.type -> true Notification.Type.ACHIEVEMENT_SEEING_RED.type -> true
@ -115,7 +114,7 @@ class MainNotificationsManager: NotificationsManager {
private fun readNotification(notification: Notification) { private fun readNotification(notification: Notification) {
MainScope().launchCatching { MainScope().launchCatching {
_displaynotificationEvents.send(notification) displayedNotificationEvents.send(notification)
seenNotifications[notification.id] = true seenNotifications[notification.id] = true
apiClient?.get()?.readNotification(notification.id) apiClient?.get()?.readNotification(notification.id)
} }

View file

@ -121,9 +121,9 @@ class PurchaseHandler(
} }
fun startListening() { fun startListening() {
if (billingClient.connectionState == BillingClient.ConnectionState.CONNECTING if (billingClient.connectionState == BillingClient.ConnectionState.CONNECTING ||
|| billingClient.connectionState == BillingClient.ConnectionState.CONNECTED billingClient.connectionState == BillingClient.ConnectionState.CONNECTED ||
|| billingClientState == BillingClientState.UNAVAILABLE billingClientState == BillingClientState.UNAVAILABLE
) { ) {
// Don't connect again if it's already connected // Don't connect again if it's already connected
return return
@ -209,9 +209,11 @@ class PurchaseHandler(
} }
billingClientState.canMaybePurchase && billingClient.isReady billingClientState.canMaybePurchase && billingClient.isReady
} }
val params = QueryProductDetailsParams.newBuilder().setProductList(skus.map { val params = QueryProductDetailsParams.newBuilder().setProductList(
Product.newBuilder().setProductId(it).setProductType(type).build() skus.map {
}).build() Product.newBuilder().setProductId(it).setProductType(type).build()
}
).build()
val skuDetailsResult = withContext(Dispatchers.IO) { val skuDetailsResult = withContext(Dispatchers.IO) {
billingClient.queryProductDetails(params) billingClient.queryProductDetails(params)
} }
@ -230,12 +232,14 @@ class PurchaseHandler(
addGift(skuDetails.productId, it, recipientUsername ?: it) addGift(skuDetails.productId, it, recipientUsername ?: it)
} }
val flowParams = val flowParams =
BillingFlowParams.newBuilder().setProductDetailsParamsList(listOf(skuDetails).map { BillingFlowParams.newBuilder().setProductDetailsParamsList(
BillingFlowParams.ProductDetailsParams.newBuilder() listOf(skuDetails).map {
.setProductDetails(skuDetails).setOfferToken( BillingFlowParams.ProductDetailsParams.newBuilder()
skuDetails.subscriptionOfferDetails?.first()?.offerToken ?: "" .setProductDetails(skuDetails).setOfferToken(
).build() skuDetails.subscriptionOfferDetails?.first()?.offerToken ?: ""
}).build() ).build()
}
).build()
billingClient.launchBillingFlow(activity, flowParams) billingClient.launchBillingFlow(activity, flowParams)
} }
@ -555,9 +559,11 @@ class PurchaseHandler(
} }
suspend fun retryUntil( suspend fun retryUntil(
times: Int = Int.MAX_VALUE, initialDelay: Long = 100, // 0.1 second times: Int = Int.MAX_VALUE,
initialDelay: Long = 100, // 0.1 second
maxDelay: Long = 1000, // 1 second maxDelay: Long = 1000, // 1 second
factor: Double = 2.0, block: suspend () -> Boolean factor: Double = 2.0,
block: suspend () -> Boolean
) { ) {
var currentDelay = initialDelay var currentDelay = initialDelay
repeat(times - 1) { repeat(times - 1) {

View file

@ -27,4 +27,4 @@ object PurchaseTypes {
Subscription6MonthNoRenew, Subscription6MonthNoRenew,
Subscription12MonthNoRenew Subscription12MonthNoRenew
) )
} }

View file

@ -27,39 +27,39 @@ class SoundFileLoader(private val context: Context) {
@SuppressLint("SetWorldReadable", "ReturnCount") @SuppressLint("SetWorldReadable", "ReturnCount")
suspend fun download(files: List<SoundFile>): List<SoundFile> { suspend fun download(files: List<SoundFile>): List<SoundFile> {
return files.map { audioFile -> return files.map { audioFile ->
withContext(Dispatchers.IO) { withContext(Dispatchers.IO) {
val file = File(getFullAudioFilePath(audioFile)) val file = File(getFullAudioFilePath(audioFile))
if (file.exists() && file.length() > 5000) { if (file.exists() && file.length() > 5000) {
// Important, or else the MediaPlayer can't access this file // Important, or else the MediaPlayer can't access this file
file.setReadable(true, false)
audioFile.file = file
return@withContext audioFile
}
val request = Request.Builder().url(audioFile.webUrl).build()
val response: Response
try {
response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException()
}
} catch (io: IOException) {
return@withContext audioFile
}
try {
val sink = file.sink().buffer()
sink.writeAll(response.body!!.source())
sink.flush()
sink.close()
} catch (io: IOException) {
return@withContext audioFile
}
file.setReadable(true, false) file.setReadable(true, false)
audioFile.file = file audioFile.file = file
return@withContext audioFile return@withContext audioFile
} }
val request = Request.Builder().url(audioFile.webUrl).build()
val response: Response
try {
response = client.newCall(request).execute()
if (!response.isSuccessful) {
throw IOException()
}
} catch (io: IOException) {
return@withContext audioFile
}
try {
val sink = file.sink().buffer()
sink.writeAll(response.body!!.source())
sink.flush()
sink.close()
} catch (io: IOException) {
return@withContext audioFile
}
file.setReadable(true, false)
audioFile.file = file
return@withContext audioFile
}
} }
} }

View file

@ -99,9 +99,9 @@ class TaskAlarmManager(
private fun setAlarmForRemindersItem(reminderItemTask: Task, remindersItem: RemindersItem?) { private fun setAlarmForRemindersItem(reminderItemTask: Task, remindersItem: RemindersItem?) {
val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant() val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant()
val zonedTime = remindersItem?.getLocalZonedDateTimeInstant() val zonedTime = remindersItem?.getLocalZonedDateTimeInstant()
if (remindersItem == null if (remindersItem == null ||
|| (reminderItemTask.type == TaskType.DAILY && zonedTime?.isBefore(now) == true && reminderItemTask.nextDue?.firstOrNull() != null) (reminderItemTask.type == TaskType.DAILY && zonedTime?.isBefore(now) == true && reminderItemTask.nextDue?.firstOrNull() != null) ||
|| (reminderItemTask.type == TaskType.TODO && zonedTime?.isBefore(now) == true) (reminderItemTask.type == TaskType.TODO && zonedTime?.isBefore(now) == true)
) { ) {
return return
} }
@ -226,7 +226,7 @@ class TaskAlarmManager(
try { try {
alarmManager?.setAlarmClock(AlarmClockInfo(time, pendingIntent), pendingIntent) alarmManager?.setAlarmClock(AlarmClockInfo(time, pendingIntent), pendingIntent)
} catch (ex: Exception) { } catch (ex: Exception) {
when(ex) { when (ex) {
is IllegalStateException, is SecurityException -> { is IllegalStateException, is SecurityException -> {
alarmManager?.setWindow(AlarmManager.RTC_WAKEUP, time, 60000, pendingIntent) alarmManager?.setWindow(AlarmManager.RTC_WAKEUP, time, 60000, pendingIntent)
} }

View file

@ -94,11 +94,13 @@ class TaskDescriptionBuilder(private val context: Context) {
"" ""
} }
} }
Frequency.YEARLY -> " " + context.getString(R.string.on_x, Frequency.YEARLY -> " " + context.getString(
R.string.on_x,
task.startDate?.let { task.startDate?.let {
val flags = DateUtils.FORMAT_SHOW_DATE + DateUtils.FORMAT_NO_YEAR val flags = DateUtils.FORMAT_SHOW_DATE + DateUtils.FORMAT_NO_YEAR
DateUtils.formatDateTime(context, it.time, flags) DateUtils.formatDateTime(context, it.time, flags)
} ?: "") } ?: ""
)
else -> "" else -> ""
} }
} }

View file

@ -24,7 +24,7 @@ class HabiticaFirebaseMessagingService : FirebaseMessagingService() {
if (remoteMessage.data["identifier"]?.contains(PushNotificationManager.WON_CHALLENGE_PUSH_NOTIFICATION_KEY) == true) { if (remoteMessage.data["identifier"]?.contains(PushNotificationManager.WON_CHALLENGE_PUSH_NOTIFICATION_KEY) == true) {
// if (this::userRepository.isInitialized) { // if (this::userRepository.isInitialized) {
// userRepository.retrieveUser(true).subscribe({}, RxErrorHandler.handleEmptyError()) // userRepository.retrieveUser(true).subscribe({}, RxErrorHandler.handleEmptyError())
// } // }
} }
} }

View file

@ -59,7 +59,7 @@ class PushNotificationManager(
addRefreshToken() addRefreshToken()
} catch (_: IOException) { } catch (_: IOException) {
// catchy catch-catch // catchy catch-catch
} catch (_: Exception){ } catch (_: Exception) {
// catchy catch-catch-cat, I'm out of breath. // catchy catch-catch-cat, I'm out of breath.
} }
} }
@ -85,7 +85,7 @@ class PushNotificationManager(
if (this.refreshedToken.isEmpty() || !userHasPushDevice()) { if (this.refreshedToken.isEmpty() || !userHasPushDevice()) {
return return
} }
apiClient.deletePushDevice(refreshedToken) apiClient.deletePushDevice(refreshedToken)
} }
private fun userHasPushDevice(): Boolean { private fun userHasPushDevice(): Boolean {
@ -133,7 +133,8 @@ class PushNotificationManager(
val remoteMessageIdentifier = remoteMessage.data["identifier"] val remoteMessageIdentifier = remoteMessage.data["identifier"]
val notificationFactory = HabiticaLocalNotificationFactory() val notificationFactory = HabiticaLocalNotificationFactory()
val notification = notificationFactory.build(remoteMessageIdentifier, val notification = notificationFactory.build(
remoteMessageIdentifier,
context context
) )
if (pushNotificationManager?.userIsSubscribedToNotificationType(remoteMessageIdentifier) != false) { if (pushNotificationManager?.userIsSubscribedToNotificationType(remoteMessageIdentifier) != false) {

View file

@ -13,7 +13,7 @@ import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class DisplayItemDropUseCase @Inject class DisplayItemDropUseCase @Inject
constructor(private val soundManager: SoundManager): constructor(private val soundManager: SoundManager) :
UseCase<DisplayItemDropUseCase.RequestValues, Unit>() { UseCase<DisplayItemDropUseCase.RequestValues, Unit>() {
override suspend fun run(requestValues: RequestValues) { override suspend fun run(requestValues: RequestValues) {

View file

@ -20,7 +20,8 @@ import javax.inject.Inject
class HatchPetUseCase @Inject class HatchPetUseCase @Inject
constructor( constructor(
private val inventoryRepository: InventoryRepository) : UseCase<HatchPetUseCase.RequestValues, Items?>() { private val inventoryRepository: InventoryRepository
) : UseCase<HatchPetUseCase.RequestValues, Items?>() {
override suspend fun run(requestValues: RequestValues): Items? { override suspend fun run(requestValues: RequestValues): Items? {
return inventoryRepository.hatchPet(requestValues.egg, requestValues.potion) { return inventoryRepository.hatchPet(requestValues.egg, requestValues.potion) {
val petWrapper = View.inflate(requestValues.context, R.layout.pet_imageview, null) as? FrameLayout val petWrapper = View.inflate(requestValues.context, R.layout.pet_imageview, null) as? FrameLayout

View file

@ -12,4 +12,4 @@ abstract class UseCase<Q : UseCase.RequestValues?, T> {
} }
interface RequestValues interface RequestValues
} }

View file

@ -52,10 +52,10 @@ open class Member : RealmObject(), Avatar, BaseMainObject, Assignable {
} else this.profile?.name ?: "" } else this.profile?.name ?: ""
override val identifiableName: String override val identifiableName: String
get() = username ?: "" get() = username ?: ""
override val avatar: Avatar override val avatar: Avatar
get() = this get() = this
val petsFoundCount: Int val petsFoundCount: Int
get() = this.items?.pets?.size ?: 0 get() = this.items?.pets?.size ?: 0

View file

@ -6,7 +6,8 @@ import io.realm.RealmObject
import io.realm.annotations.RealmClass import io.realm.annotations.RealmClass
@RealmClass(embedded = true) @RealmClass(embedded = true)
open class MemberPreferences : RealmObject(), open class MemberPreferences :
RealmObject(),
AvatarPreferences { AvatarPreferences {
override var hair: Hair? = null override var hair: Hair? = null

View file

@ -10,11 +10,11 @@ import com.habitrpg.android.habitica.databinding.FragmentGemPurchaseBinding
import com.habitrpg.android.habitica.databinding.FragmentSubscriptionBinding import com.habitrpg.android.habitica.databinding.FragmentSubscriptionBinding
import com.habitrpg.android.habitica.databinding.PurchaseGemViewBinding import com.habitrpg.android.habitica.databinding.PurchaseGemViewBinding
import com.habitrpg.android.habitica.extensions.DateUtils import com.habitrpg.android.habitica.extensions.DateUtils
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment import com.habitrpg.android.habitica.ui.fragments.PromoInfoFragment
import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionFragment import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionFragment
import com.habitrpg.android.habitica.ui.views.promo.PromoMenuView import com.habitrpg.android.habitica.ui.views.promo.PromoMenuView
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.util.Date import java.util.Date
import java.util.Locale import java.util.Locale

View file

@ -166,7 +166,7 @@ open class ShopItem : RealmObject(), BaseObject {
item.notes = customization.notes item.notes = customization.notes
item.value = customization.price ?: 0 item.value = customization.price ?: 0
item.path = customization.path item.path = customization.path
item.unlockPath = customization.unlockPath item.unlockPath = customization.unlockPath
item.pinType = customization.type item.pinType = customization.type
if (customization.type == "background") { if (customization.type == "background") {
item.purchaseType = "background" item.purchaseType = "background"

View file

@ -13,9 +13,9 @@ import io.realm.annotations.PrimaryKey
open class Group : RealmObject(), BaseMainObject { open class Group : RealmObject(), BaseMainObject {
val isGroupPlan: Boolean val isGroupPlan: Boolean
get() { get() {
return purchased?.isActive == true return purchased?.isActive == true
} }
override val realmClass: Class<Group> override val realmClass: Class<Group>
get() = Group::class.java get() = Group::class.java
override val primaryIdentifier: String? override val primaryIdentifier: String?

View file

@ -8,7 +8,7 @@ import com.habitrpg.android.habitica.R
@io.realm.annotations.RealmClass(embedded = true) @io.realm.annotations.RealmClass(embedded = true)
open class Days() : io.realm.RealmObject(), Parcelable { open class Days() : io.realm.RealmObject(), Parcelable {
val isEveryDay: Boolean val isEveryDay: Boolean
get() = m && t && w && th && f && s && su get() = m && t && w && th && f && s && su
val isOnlyWeekdays: Boolean val isOnlyWeekdays: Boolean
get() = m && t && w && th && f && !s && !su get() = m && t && w && th && f && !s && !su
val isOnlyWeekends: Boolean val isOnlyWeekends: Boolean
@ -66,4 +66,4 @@ open class Days() : io.realm.RealmObject(), Parcelable {
return arrayOfNulls(size) return arrayOfNulls(size)
} }
} }
} }

View file

@ -96,7 +96,6 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
.equals(ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault()).toLocalDate()) .equals(ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault()).toLocalDate())
} }
// Needed for offline creating/updating // Needed for offline creating/updating
var isSaving: Boolean = false var isSaving: Boolean = false
var hasErrored: Boolean = false var hasErrored: Boolean = false
@ -307,14 +306,14 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant() val now = ZonedDateTime.now().withZoneSameLocal(ZoneId.systemDefault())?.toInstant()
val nextDate = nextDue?.firstOrNull() val nextDate = nextDue?.firstOrNull()
//If task !isDisplayedActive or if isDisplayedActive but reminder passed, // If task !isDisplayedActive or if isDisplayedActive but reminder passed,
//set a updated reminder with nextDate // set a updated reminder with nextDate
return if (nextDate != null && (!isDisplayedActive || remindersItem.getLocalZonedDateTimeInstant()?.isBefore(now) == true)) { return if (nextDate != null && (!isDisplayedActive || remindersItem.getLocalZonedDateTimeInstant()?.isBefore(now) == true)) {
val nextDueCalendar = GregorianCalendar() val nextDueCalendar = GregorianCalendar()
nextDueCalendar.time = nextDate nextDueCalendar.time = nextDate
parse(oldTime) parse(oldTime)
?.withYear(nextDueCalendar.get(Calendar.YEAR)) ?.withYear(nextDueCalendar.get(Calendar.YEAR))
?.withMonth(nextDueCalendar.get(Calendar.MONTH) + 1) //+1 to handle Gregorian Calendar month range from 0-11 ?.withMonth(nextDueCalendar.get(Calendar.MONTH) + 1) // +1 to handle Gregorian Calendar month range from 0-11
?.withDayOfMonth(nextDueCalendar.get(Calendar.DAY_OF_MONTH)) ?.withDayOfMonth(nextDueCalendar.get(Calendar.DAY_OF_MONTH))
} else { } else {
return parse(oldTime) return parse(oldTime)
@ -392,7 +391,7 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
} }
fun isBeingEdited(task: Task): Boolean { fun isBeingEdited(task: Task): Boolean {
when { when {
text != task.text -> return true text != task.text -> return true
notes != task.notes -> return true notes != task.notes -> return true

View file

@ -8,7 +8,7 @@ import io.realm.annotations.RealmClass
import java.util.Date import java.util.Date
@RealmClass(embedded = true) @RealmClass(embedded = true)
open class GroupAssignedDetails: RealmObject(), BaseObject { open class GroupAssignedDetails : RealmObject(), BaseObject {
var assignedDate: Date? = null var assignedDate: Date? = null
var assignedUsername: String? = null var assignedUsername: String? = null
var assignedUserID: String? = null var assignedUserID: String? = null

View file

@ -2,4 +2,4 @@ package com.habitrpg.android.habitica.models.tasks
class TaskList { class TaskList {
var tasks: MutableMap<String, Task> = mutableMapOf() var tasks: MutableMap<String, Task> = mutableMapOf()
} }

View file

@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.models.user
import com.habitrpg.android.habitica.models.BaseObject import com.habitrpg.android.habitica.models.BaseObject
import io.realm.RealmObject import io.realm.RealmObject
open class Permissions: RealmObject(), BaseObject { open class Permissions : RealmObject(), BaseObject {
var userSupport: Boolean = false var userSupport: Boolean = false
var fullAccess: Boolean = false var fullAccess: Boolean = false

View file

@ -56,17 +56,17 @@ open class SubscriptionPlan : RealmObject(), BaseObject {
*/ */
val subMonthCount: Int val subMonthCount: Int
get() { get() {
return when (planId) { return when (planId) {
"basic_earned" -> 1 "basic_earned" -> 1
"basic_3mo" -> 3 "basic_3mo" -> 3
"basic_6mo" -> 6 "basic_6mo" -> 6
"google_6mo" -> 6 "google_6mo" -> 6
"basic_12mo" -> 12 "basic_12mo" -> 12
"group_plan_auto" -> 1 "group_plan_auto" -> 1
else -> 0 else -> 0
}
} }
}
val monthsUntilNextHourglass: Int val monthsUntilNextHourglass: Int
get() { get() {

View file

@ -5,4 +5,4 @@ enum class UserQuestStatus {
QUEST_COLLECT, QUEST_COLLECT,
QUEST_BOSS, QUEST_BOSS,
QUEST_UNKNOWN QUEST_UNKNOWN
} }

View file

@ -6,7 +6,7 @@ import io.realm.RealmObject
import io.realm.annotations.RealmClass import io.realm.annotations.RealmClass
@RealmClass(embedded = true) @RealmClass(embedded = true)
open class UserTaskPreferences: RealmObject(), BaseObject { open class UserTaskPreferences : RealmObject(), BaseObject {
var confirmScoreNotes: Boolean = false var confirmScoreNotes: Boolean = false
var mirrorGroupTasks: RealmList<String> = RealmList() var mirrorGroupTasks: RealmList<String> = RealmList()
var groupByChallenge: Boolean = false var groupByChallenge: Boolean = false

View file

@ -47,4 +47,4 @@ class UserModule {
companion object { companion object {
const val NAMED_USER_ID = "userId" const val NAMED_USER_ID = "userId"
} }
} }

View file

@ -57,4 +57,4 @@ class DeviceCommunicationService : WearableListenerService() {
"${hostConfig.userID}:${hostConfig.apiKey}".toByteArray() "${hostConfig.userID}:${hostConfig.apiKey}".toByteArray()
) )
} }
} }

View file

@ -8,8 +8,8 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.habitrpg.android.habitica.databinding.WidgetConfigureAddTaskBinding import com.habitrpg.android.habitica.databinding.WidgetConfigureAddTaskBinding
import com.habitrpg.shared.habitica.models.tasks.TaskType
import com.habitrpg.android.habitica.widget.AddTaskWidgetProvider import com.habitrpg.android.habitica.widget.AddTaskWidgetProvider
import com.habitrpg.shared.habitica.models.tasks.TaskType
class AddTaskWidgetActivity : AppCompatActivity() { class AddTaskWidgetActivity : AppCompatActivity() {

View file

@ -214,7 +214,7 @@ class ArmoireActivity : BaseActivity() {
} }
else -> { else -> {
@SuppressLint("SetTextI18n") @SuppressLint("SetTextI18n")
binding.titleView.text = "+${value} ${binding.titleView.text}" binding.titleView.text = "+$value ${binding.titleView.text}"
binding.subtitleView.text = getString(R.string.armoireExp) binding.subtitleView.text = getString(R.string.armoireExp)
binding.iconView.setImageResource(R.drawable.armoire_experience) binding.iconView.setImageResource(R.drawable.armoire_experience)
val layoutParams = RelativeLayout.LayoutParams(108.dpToPx(this), 122.dpToPx(this)) val layoutParams = RelativeLayout.LayoutParams(108.dpToPx(this), 122.dpToPx(this))

View file

@ -4,10 +4,8 @@ import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.SharedPreferences import android.content.SharedPreferences
import android.content.res.Configuration import android.content.res.Configuration
import android.content.res.Resources
import android.graphics.Bitmap import android.graphics.Bitmap
import android.net.Uri import android.net.Uri
import android.os.Build
import android.os.Bundle import android.os.Bundle
import android.provider.MediaStore import android.provider.MediaStore
import android.view.LayoutInflater import android.view.LayoutInflater
@ -40,7 +38,6 @@ import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
import com.habitrpg.common.habitica.helpers.LanguageHelper import com.habitrpg.common.habitica.helpers.LanguageHelper
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.Date import java.util.Date
import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
abstract class BaseActivity : AppCompatActivity() { abstract class BaseActivity : AppCompatActivity() {
@ -96,12 +93,12 @@ abstract class BaseActivity : AppCompatActivity() {
} }
lifecycleScope.launchCatching { lifecycleScope.launchCatching {
notificationsManager.displayNotificationEvents.collect { notificationsManager.displayNotificationEvents.collect {
if (ShowNotificationInteractor(this@BaseActivity, lifecycleScope).handleNotification(it)) { if (ShowNotificationInteractor(this@BaseActivity, lifecycleScope).handleNotification(it)) {
lifecycleScope.launch(ExceptionHandler.coroutine()) { lifecycleScope.launch(ExceptionHandler.coroutine()) {
userRepository.retrieveUser(false, true) userRepository.retrieveUser(false, true)
}
} }
} }
}
} }
} }
@ -252,4 +249,4 @@ abstract class BaseActivity : AppCompatActivity() {
overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out) overridePendingTransition(R.anim.activity_fade_in, R.anim.activity_fade_out)
startActivity(intent) startActivity(intent)
} }
} }

View file

@ -198,7 +198,8 @@ class BirthdayActivity : BaseActivity() {
@Composable @Composable
fun BirthdayTitle(text: String) { fun BirthdayTitle(text: String) {
Row( Row(
verticalAlignment = Alignment.CenterVertically, modifier = Modifier verticalAlignment = Alignment.CenterVertically,
modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 20.dp, bottom = 8.dp) .padding(top = 20.dp, bottom = 8.dp)
) { ) {
@ -259,7 +260,8 @@ fun BirthdayActivityView(
scaffoldState = scaffoldState scaffoldState = scaffoldState
) { padding -> ) { padding ->
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.background( .background(
Brush.verticalGradient( Brush.verticalGradient(
Pair(0.0f, colorResource(id = R.color.brand_300)), Pair(0.0f, colorResource(id = R.color.brand_300)),
@ -291,7 +293,8 @@ fun BirthdayActivityView(
) )
} }
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally, modifier = Modifier horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier
.padding(horizontal = 20.dp) .padding(horizontal = 20.dp)
.fillMaxWidth() .fillMaxWidth()
) { ) {
@ -388,16 +391,19 @@ fun BirthdayActivityView(
} else if (isPurchasing) { } else if (isPurchasing) {
CircularProgressIndicator() CircularProgressIndicator()
} else { } else {
Text(buildAnnotatedString { Text(
append("Buy for ") buildAnnotatedString {
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { append("Buy for ")
append(price) withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
} append(price)
append(" or ") }
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) { append(" or ")
append("60 Gems") withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
} append("60 Gems")
}, color = Color.White) }
},
color = Color.White
)
HabiticaButton( HabiticaButton(
Color.White, Color.White,
colorResource(R.color.brand_200), colorResource(R.color.brand_200),
@ -557,7 +563,8 @@ fun PotionGrid() {
AsyncImage( AsyncImage(
model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename( model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename(
"Pet_HatchingPotion_$potion" "Pet_HatchingPotion_$potion"
), null, Modifier.size(68.dp) ),
null, Modifier.size(68.dp)
) )
} }
} }
@ -616,11 +623,14 @@ fun HabiticaButton(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
content: @Composable () -> Unit content: @Composable () -> Unit
) { ) {
Box(contentAlignment = Alignment.Center, modifier = modifier Box(
.background(background, HabiticaTheme.shapes.medium) contentAlignment = Alignment.Center,
.clickable { onClick() } modifier = modifier
.fillMaxWidth() .background(background, HabiticaTheme.shapes.medium)
.padding(8.dp)) { .clickable { onClick() }
.fillMaxWidth()
.padding(8.dp)
) {
ProvideTextStyle( ProvideTextStyle(
value = TextStyle( value = TextStyle(
fontSize = 18.sp, fontSize = 18.sp,
@ -640,4 +650,4 @@ private fun Preview() {
val scaffoldState = rememberScaffoldState() val scaffoldState = rememberScaffoldState()
BirthdayActivityView(scaffoldState, true, false, false, "", Date(), Date(), { BirthdayActivityView(scaffoldState, true, false, false, "", Date(), Date(), {
}, {}) {} }, {}) {}
} }

View file

@ -396,15 +396,15 @@ class ChallengeFormActivity : BaseActivity() {
} }
binding.createChallengeTaskList.addOnItemTouchListener(object : binding.createChallengeTaskList.addOnItemTouchListener(object :
androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener() { androidx.recyclerview.widget.RecyclerView.SimpleOnItemTouchListener() {
override fun onInterceptTouchEvent( override fun onInterceptTouchEvent(
rv: androidx.recyclerview.widget.RecyclerView, rv: androidx.recyclerview.widget.RecyclerView,
e: MotionEvent e: MotionEvent
): Boolean { ): Boolean {
// Stop only scrolling. // Stop only scrolling.
return rv.scrollState == androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING return rv.scrollState == androidx.recyclerview.widget.RecyclerView.SCROLL_STATE_DRAGGING
} }
}) })
binding.createChallengeTaskList.adapter = challengeTasks binding.createChallengeTaskList.adapter = challengeTasks
binding.createChallengeTaskList.layoutManager = binding.createChallengeTaskList.layoutManager =
androidx.recyclerview.widget.LinearLayoutManager(this) androidx.recyclerview.widget.LinearLayoutManager(this)

View file

@ -24,7 +24,7 @@ import com.plattysoft.leonids.ParticleSystem
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
class DeathActivity: BaseActivity() { class DeathActivity : BaseActivity() {
private lateinit var binding: ActivityDeathBinding private lateinit var binding: ActivityDeathBinding
@Inject @Inject
@ -109,12 +109,11 @@ class DeathActivity: BaseActivity() {
.setScaleRange(0.5f, 0.8f) .setScaleRange(0.5f, 0.8f)
.setSpeedRange(0.01f, 0.03f) .setSpeedRange(0.01f, 0.03f)
.setFadeOut(4000, AccelerateInterpolator()) .setFadeOut(4000, AccelerateInterpolator())
.setSpeedModuleAndAngleRange(0.01f, 0.03f, startAngle, startAngle+80) .setSpeedModuleAndAngleRange(0.01f, 0.03f, startAngle, startAngle + 80)
.emit(binding.root.width / 2, positionArray[1] + (binding.heartView.height/2), 3, 6000) .emit(binding.root.width / 2, positionArray[1] + (binding.heartView.height / 2), 3, 6000)
} }
override fun onBackPressed() { override fun onBackPressed() {
moveTaskToBack(true) moveTaskToBack(true)
} }
} }

View file

@ -13,9 +13,8 @@ import com.habitrpg.android.habitica.databinding.ActivityFixcharacterBinding
import com.habitrpg.android.habitica.extensions.setTintWith import com.habitrpg.android.habitica.extensions.setTintWith
import com.habitrpg.android.habitica.models.user.Stats import com.habitrpg.android.habitica.models.user.Stats
import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import javax.inject.Inject import javax.inject.Inject
class FixCharacterValuesActivity : BaseActivity() { class FixCharacterValuesActivity : BaseActivity() {

View file

@ -147,11 +147,11 @@ class FullProfileActivity : BaseActivity() {
} }
private suspend fun refresh() { private suspend fun refresh() {
val member = socialRepository.retrieveMember(userID) val member = socialRepository.retrieveMember(userID)
if (member != null) { if (member != null) {
updateView(member) updateView(member)
} }
this@FullProfileActivity.member.value = member this@FullProfileActivity.member.value = member
} }
override fun onDestroy() { override fun onDestroy() {
@ -174,21 +174,27 @@ class FullProfileActivity : BaseActivity() {
} }
menu.setGroupVisible(R.id.admin_items, isModerator) menu.setGroupVisible(R.id.admin_items, isModerator)
if (isModerator) { if (isModerator) {
menu.findItem(R.id.ban_user)?.title = getString(if (member.value?.authentication?.blocked == true) { menu.findItem(R.id.ban_user)?.title = getString(
R.string.unban_user if (member.value?.authentication?.blocked == true) {
} else { R.string.unban_user
R.string.ban_user } else {
}) R.string.ban_user
menu.findItem(R.id.shadow_mute_user)?.title = getString(if (member.value?.flags?.chatShadowMuted == true) { }
R.string.unshadowmute_user )
} else { menu.findItem(R.id.shadow_mute_user)?.title = getString(
R.string.shadow_mute_user if (member.value?.flags?.chatShadowMuted == true) {
}) R.string.unshadowmute_user
menu.findItem(R.id.mute_user)?.title = getString(if (member.value?.flags?.chatRevoked == true) { } else {
R.string.unmute_user R.string.shadow_mute_user
} else { }
R.string.mute_user )
}) menu.findItem(R.id.mute_user)?.title = getString(
if (member.value?.flags?.chatRevoked == true) {
R.string.unmute_user
} else {
R.string.mute_user
}
)
} }
return super.onCreateOptionsMenu(menu) return super.onCreateOptionsMenu(menu)
} }
@ -357,7 +363,7 @@ class FullProfileActivity : BaseActivity() {
if (imageUrl == null || imageUrl.isEmpty()) { if (imageUrl == null || imageUrl.isEmpty()) {
binding.profileImage.visibility = View.GONE binding.profileImage.visibility = View.GONE
} else { } else {
//binding.profileImage.load(imageUrl) // binding.profileImage.load(imageUrl)
} }
val blurbText = profile.blurb val blurbText = profile.blurb

View file

@ -81,10 +81,12 @@ class LoginActivity : BaseActivity() {
showValidationError(R.string.login_validation_error_fieldsmissing) showValidationError(R.string.login_validation_error_fieldsmissing)
return return
} }
lifecycleScope.launch(ExceptionHandler.coroutine { lifecycleScope.launch(
hideProgress() ExceptionHandler.coroutine {
ExceptionHandler.reportError(it) hideProgress()
}) { ExceptionHandler.reportError(it)
}
) {
val response = apiClient.connectUser(username, password) val response = apiClient.connectUser(username, password)
if (response != null) { if (response != null) {
handleAuthResponse(response) handleAuthResponse(response)
@ -112,10 +114,12 @@ class LoginActivity : BaseActivity() {
) )
return return
} }
lifecycleScope.launch(ExceptionHandler.coroutine { lifecycleScope.launch(
hideProgress() ExceptionHandler.coroutine {
ExceptionHandler.reportError(it) hideProgress()
}) { ExceptionHandler.reportError(it)
}
) {
val response = apiClient.registerUser(username, email, password, confirmPassword) val response = apiClient.registerUser(username, email, password, confirmPassword)
if (response != null) { if (response != null) {
handleAuthResponse(response) handleAuthResponse(response)

View file

@ -96,9 +96,11 @@ class ReportMessageActivity : BaseActivity() {
} }
isReporting = true isReporting = true
messageID?.let { messageID?.let {
lifecycleScope.launch(ExceptionHandler.coroutine { lifecycleScope.launch(
isReporting = false ExceptionHandler.coroutine {
}) { isReporting = false
}
) {
socialRepository.flagMessage(messageID ?: "", binding.additionalInfoEdittext.text.toString(), groupID) socialRepository.flagMessage(messageID ?: "", binding.additionalInfoEdittext.text.toString(), groupID)
finish() finish()
} }

View file

@ -206,7 +206,6 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
override fun onPageScrollStateChanged(state: Int) = Unit override fun onPageScrollStateChanged(state: Int) = Unit
private var hasCompleted = false private var hasCompleted = false
private fun onUserReceived(user: User?) { private fun onUserReceived(user: User?) {
if (completedSetup && !hasCompleted) { if (completedSetup && !hasCompleted) {

View file

@ -60,7 +60,7 @@ class SkillTasksActivity : BaseActivity() {
else -> TaskType.TODO else -> TaskType.TODO
} }
fragment.onTaskSelection = { fragment.onTaskSelection = {
taskSelected(it) taskSelected(it)
} }
viewFragmentsDictionary.put(position, fragment) viewFragmentsDictionary.put(position, fragment)
return fragment return fragment

View file

@ -117,7 +117,7 @@ class TaskFormActivity : BaseActivity() {
if (granted) { if (granted) {
pushNotificationManager.addPushDeviceUsingStoredToken() pushNotificationManager.addPushDeviceUsingStoredToken()
} else { } else {
//If user denies notification settings originally - they must manually enable it through notification settings. // If user denies notification settings originally - they must manually enable it through notification settings.
val alert = HabiticaAlertDialog(this) val alert = HabiticaAlertDialog(this)
alert.setTitle(R.string.push_notification_system_settings_title) alert.setTitle(R.string.push_notification_system_settings_title)
alert.setMessage(R.string.push_notification_system_settings_description) alert.setMessage(R.string.push_notification_system_settings_description)
@ -340,7 +340,8 @@ class TaskFormActivity : BaseActivity() {
HabiticaTheme { HabiticaTheme {
TaskDifficultySelector( TaskDifficultySelector(
viewModel.taskDifficulty.value, viewModel.taskDifficulty.value,
onSelect = { viewModel.taskDifficulty.value = it }) onSelect = { viewModel.taskDifficulty.value = it }
)
} }
} }
@ -351,32 +352,38 @@ class TaskFormActivity : BaseActivity() {
viewModel.habitScoringPositive.value, viewModel.habitScoringPositive.value,
viewModel.habitScoringNegative.value, viewModel.habitScoringNegative.value,
{ viewModel.habitScoringPositive.value = !viewModel.habitScoringPositive.value }, { viewModel.habitScoringPositive.value = !viewModel.habitScoringPositive.value },
{ viewModel.habitScoringNegative.value = !viewModel.habitScoringNegative.value }) { viewModel.habitScoringNegative.value = !viewModel.habitScoringNegative.value }
)
} }
} }
binding.habitResetStreakButtons.setContent { binding.habitResetStreakButtons.setContent {
HabiticaTheme { HabiticaTheme {
TaskFormSelector( TaskFormSelector(
viewModel.habitResetOption.value, listOf( viewModel.habitResetOption.value,
listOf(
LabeledValue(getString(R.string.repeat_daily), HabitResetOption.DAILY), LabeledValue(getString(R.string.repeat_daily), HabitResetOption.DAILY),
LabeledValue(getString(R.string.weekly), HabitResetOption.WEEKLY), LabeledValue(getString(R.string.weekly), HabitResetOption.WEEKLY),
LabeledValue(getString(R.string.monthly), HabitResetOption.MONTHLY) LabeledValue(getString(R.string.monthly), HabitResetOption.MONTHLY)
), { viewModel.habitResetOption.value = it }, columnSize = 3 ),
{ viewModel.habitResetOption.value = it }, columnSize = 3
) )
} }
} }
} }
binding.statsSelector.setContent { binding.statsSelector.setContent {
HabiticaTheme { HabiticaTheme {
TaskFormSelector(viewModel.selectedAttribute.value, listOf( TaskFormSelector(
LabeledValue(getString(R.string.strength), Attribute.STRENGTH), viewModel.selectedAttribute.value,
LabeledValue(getString(R.string.constitution), Attribute.CONSTITUTION), listOf(
LabeledValue(getString(R.string.intelligence), Attribute.INTELLIGENCE), LabeledValue(getString(R.string.strength), Attribute.STRENGTH),
LabeledValue(getString(R.string.perception), Attribute.PERCEPTION) LabeledValue(getString(R.string.constitution), Attribute.CONSTITUTION),
), { viewModel.selectedAttribute.value = it }) LabeledValue(getString(R.string.intelligence), Attribute.INTELLIGENCE),
LabeledValue(getString(R.string.perception), Attribute.PERCEPTION)
),
{ viewModel.selectedAttribute.value = it }
)
} }
} }

View file

@ -240,7 +240,8 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
.fillMaxWidth() .fillMaxWidth()
) { ) {
Image(HabiticaIconsHelper.imageOfGold().asImageBitmap(), null) Image(HabiticaIconsHelper.imageOfGold().asImageBitmap(), null)
Text("${task?.value}", Text(
"${task?.value}",
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Medium, fontWeight = FontWeight.Medium,
color = darkestColor color = darkestColor
@ -285,7 +286,8 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
for (item in task?.group?.assignedUsersDetail ?: emptyList()) { for (item in task?.group?.assignedUsersDetail ?: emptyList()) {
val member = viewModel.getMember(item.assignedUserID).collectAsState(null) val member = viewModel.getMember(item.assignedUserID).collectAsState(null)
UserRow( UserRow(
item.assignedUsername ?: "", member.value, Modifier item.assignedUsername ?: "", member.value,
Modifier
.padding(vertical = 4.dp) .padding(vertical = 4.dp)
.background( .background(
HabiticaTheme.colors.windowBackgroundFor(task), HabiticaTheme.colors.windowBackgroundFor(task),
@ -295,9 +297,11 @@ fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
.heightIn(min = 24.dp) .heightIn(min = 24.dp)
.fillMaxWidth(), .fillMaxWidth(),
color = darkestColor, color = darkestColor,
extraContent = if (item.completed) ({ extraContent = if (item.completed) (
CompletedAt(item.completedDate) {
}) else null CompletedAt(item.completedDate)
}
) else null
) )
} }
task?.group?.assignedUsersDetail?.find { it.assignedUserID == viewModel.userViewModel.userID } task?.group?.assignedUsersDetail?.find { it.assignedUserID == viewModel.userViewModel.userID }
@ -339,4 +343,4 @@ private fun String.makeBoldComposable(): AnnotatedString {
@Composable @Composable
private fun TaskSummaryViewPreview() { private fun TaskSummaryViewPreview() {
TaskSummaryView(TaskSummaryViewModel("")) TaskSummaryView(TaskSummaryViewModel(""))
} }

View file

@ -11,9 +11,9 @@ import com.habitrpg.android.habitica.databinding.AchievementSectionHeaderBinding
import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.Achievement import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.models.QuestAchievement import com.habitrpg.android.habitica.models.QuestAchievement
import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDetailDialog
import com.habitrpg.common.habitica.extensions.loadImage import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.common.habitica.views.PixelArtView import com.habitrpg.common.habitica.views.PixelArtView
import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDetailDialog
class AchievementsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class AchievementsAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

View file

@ -25,9 +25,9 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R
var equipmentList: MutableList<Equipment> = var equipmentList: MutableList<Equipment> =
ArrayList() ArrayList()
set(value) { set(value) {
field = value field = value
notifyDataSetChanged() notifyDataSetChanged()
} }
var activeEquipment: String? = null var activeEquipment: String? = null
set(value) { set(value) {
field = value field = value
@ -119,7 +119,8 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R
priceLabel?.text = itemValue.toString() priceLabel?.text = itemValue.toString()
(dialogContent.findViewById<View>(R.id.gem_icon) as? ImageView)?.setImageBitmap( (dialogContent.findViewById<View>(R.id.gem_icon) as? ImageView)?.setImageBitmap(
HabiticaIconsHelper.imageOfGem()) HabiticaIconsHelper.imageOfGem()
)
val dialog = HabiticaAlertDialog(itemView.context) val dialog = HabiticaAlertDialog(itemView.context)
dialog.addButton(R.string.purchase_button, true) { _, _ -> dialog.addButton(R.string.purchase_button, true) { _, _ ->
@ -153,4 +154,4 @@ class CustomizationEquipmentRecyclerViewAdapter : androidx.recyclerview.widget.R
} }
} }
} }
} }

View file

@ -113,9 +113,15 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
val isOwned = ownedCustomizations.contains(customization.id) val isOwned = ownedCustomizations.contains(customization.id)
val isUsable = customization.isUsable(isOwned) val isUsable = customization.isUsable(isOwned)
if (customization.availableFrom != null || customization.availableUntil != null) { if (customization.availableFrom != null || customization.availableUntil != null) {
if (((customization.availableFrom?.compareTo(today) if ((
?: 0) > 0 || (customization.availableUntil?.compareTo(today) (
?: 0) < 0) && !isUsable customization.availableFrom?.compareTo(today)
?: 0
) > 0 || (
customization.availableUntil?.compareTo(today)
?: 0
) < 0
) && !isUsable
) { ) {
continue continue
} }
@ -218,7 +224,7 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
return return
} }
if (customization?.type == "background"){ if (customization?.type == "background") {
val alert = HabiticaAlertDialog(context = itemView.context) val alert = HabiticaAlertDialog(context = itemView.context)
val purchasedCustomizationView: View = LayoutInflater.from(itemView.context).inflate(R.layout.purchased_equip_dialog, null) val purchasedCustomizationView: View = LayoutInflater.from(itemView.context).inflate(R.layout.purchased_equip_dialog, null)
val layerMap = EnumMap<AvatarView.LayerType, String>(AvatarView.LayerType::class.java) val layerMap = EnumMap<AvatarView.LayerType, String>(AvatarView.LayerType::class.java)
@ -263,12 +269,12 @@ class CustomizationRecyclerViewAdapter() : androidx.recyclerview.widget.Recycler
var additionalSetItems: List<Customization>? = null var additionalSetItems: List<Customization>? = null
var buttonWidth: Int var buttonWidth: Int
get() = binding.purchaseSetButton.width get() = binding.purchaseSetButton.width
set(value) { set(value) {
val params = binding.purchaseSetButton.layoutParams val params = binding.purchaseSetButton.layoutParams
params.width = value params.width = value
binding.purchaseSetButton.layoutParams = params binding.purchaseSetButton.layoutParams = params
} }
init { init {
binding.purchaseSetButton.setOnClickListener(this) binding.purchaseSetButton.setOnClickListener(this)

View file

@ -111,7 +111,6 @@ class NavigationDrawerAdapter(tintColor: Int, backgroundTintColor: Int) : Recycl
private fun getItem(position: Int) = items.filter { it.isVisible }[position] private fun getItem(position: Int) = items.filter { it.isVisible }[position]
override fun getItemCount(): Int = items.count { it.isVisible } override fun getItemCount(): Int = items.count { it.isVisible }
override fun getItemViewType(position: Int): Int { override fun getItemViewType(position: Int): Int {

View file

@ -99,11 +99,11 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter<OwnedI
binding.ownedTextView.text = ownedItem.numberOwned.toString() binding.ownedTextView.text = ownedItem.numberOwned.toString()
val disabled = if (isHatching) { val disabled = if (isHatching) {
!this.canHatch !this.canHatch
} else false } else false
val imageName = if (item != null) { val imageName = if (item != null) {
getImageName(item = item) } getImageName(item = item)
else { } else {
getImageName(ownedItem = ownedItem) getImageName(ownedItem = ownedItem)
} }
binding.imageView.loadImage(imageName) binding.imageView.loadImage(imageName)
@ -130,7 +130,7 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter<OwnedI
"inventory_quest_scroll_" + item.key "inventory_quest_scroll_" + item.key
} }
is SpecialItem -> { is SpecialItem -> {
//Mystery Item (Inventory Present) // Mystery Item (Inventory Present)
val sdf = SimpleDateFormat("MM", Locale.getDefault()) val sdf = SimpleDateFormat("MM", Locale.getDefault())
val month = sdf.format(Date()) val month = sdf.format(Date())
"inventory_present_$month" "inventory_present_$month"
@ -153,8 +153,8 @@ class ItemRecyclerAdapter(val context: Context) : BaseRecyclerViewAdapter<OwnedI
val menu = BottomSheetMenu(context) val menu = BottomSheetMenu(context)
menu.setTitle(item?.text) menu.setTitle(item?.text)
val imageName = if (item != null) { val imageName = if (item != null) {
getImageName(item = item) } getImageName(item = item)
else { } else {
getImageName(ownedItem = ownedItem) getImageName(ownedItem = ownedItem)
} }
menu.setImage(imageName) menu.setImage(imageName)

View file

@ -43,14 +43,13 @@ class StableRecyclerAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {
field = value field = value
notifyDataSetChanged() notifyDataSetChanged()
} }
var onEquip: ((String) -> Unit)? = null var onEquip: ((String) -> Unit)? = null
private var existingMounts: List<Mount>? = null private var existingMounts: List<Mount>? = null
private var ownedMounts: Map<String, OwnedMount>? = null private var ownedMounts: Map<String, OwnedMount>? = null
private var ownedItems: Map<String, OwnedItem>? = null private var ownedItems: Map<String, OwnedItem>? = null
private var ownsSaddles: Boolean = false private var ownsSaddles: Boolean = false
private var itemList: List<Any> = ArrayList() private var itemList: List<Any> = ArrayList()
private fun canRaiseToMount(pet: Pet): Boolean { private fun canRaiseToMount(pet: Pet): Boolean {
if (pet.type == "special") return false if (pet.type == "special") return false
for (mount in existingMounts ?: emptyList()) { for (mount in existingMounts ?: emptyList()) {

View file

@ -8,9 +8,9 @@ import com.habitrpg.android.habitica.databinding.ProfileAchievementItemBinding
import com.habitrpg.android.habitica.extensions.inflate import com.habitrpg.android.habitica.extensions.inflate
import com.habitrpg.android.habitica.models.Achievement import com.habitrpg.android.habitica.models.Achievement
import com.habitrpg.android.habitica.ui.activities.MainActivity import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.common.habitica.extensions.loadImage
import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder import com.habitrpg.android.habitica.ui.viewHolders.SectionViewHolder
import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDetailDialog import com.habitrpg.android.habitica.ui.views.dialogs.AchievementDetailDialog
import com.habitrpg.common.habitica.extensions.loadImage
class AchievementProfileAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() { class AchievementProfileAdapter : RecyclerView.Adapter<RecyclerView.ViewHolder>() {

View file

@ -12,7 +12,6 @@ import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeFilterOptions import com.habitrpg.android.habitica.ui.fragments.social.challenges.ChallengeFilterOptions
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.common.habitica.helpers.EmojiParser import com.habitrpg.common.habitica.helpers.EmojiParser
import io.realm.OrderedRealmCollection import io.realm.OrderedRealmCollection
class ChallengesListViewAdapter( class ChallengesListViewAdapter(

View file

@ -13,12 +13,13 @@ class DailiesRecyclerViewHolder(layoutResource: Int, viewModel: TasksViewModel)
getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) }, getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) },
{ task, item -> checklistItemScoreEvents?.invoke(task, item) }, { task, item -> checklistItemScoreEvents?.invoke(task, item) },
{ {
task -> task ->
taskOpenEvents?.invoke(task.first, task.second) taskOpenEvents?.invoke(task.first, task.second)
}, { }, {
task -> task ->
brokenTaskEvents?.invoke(task) brokenTaskEvents?.invoke(task)
}, viewModel) }, viewModel
)
} else { } else {
super.onCreateViewHolder(parent, viewType) super.onCreateViewHolder(parent, viewType)
} }

View file

@ -12,12 +12,13 @@ class HabitsRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel)
HabitViewHolder( HabitViewHolder(
getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) }, getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) },
{ {
task -> task ->
taskOpenEvents?.invoke(task.first, task.second) taskOpenEvents?.invoke(task.first, task.second)
}, { }, {
task -> task ->
brokenTaskEvents?.invoke(task) brokenTaskEvents?.invoke(task)
}, viewModel) }, viewModel
)
} else { } else {
super.onCreateViewHolder(parent, viewType) super.onCreateViewHolder(parent, viewType)
} }

View file

@ -73,9 +73,10 @@ class RewardsRecyclerViewAdapter(
} }
}, },
{ task -> taskOpenEvents?.invoke(task.first, task.second) }, { { task -> taskOpenEvents?.invoke(task.first, task.second) }, {
task -> task ->
brokenTaskEvents?.invoke(task) brokenTaskEvents?.invoke(task)
}, viewModel) }, viewModel
)
} else { } else {
val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false)) val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false))
viewHolder.purchaseCardAction = { purchaseCardEvents?.invoke(it) } viewHolder.purchaseCardAction = { purchaseCardEvents?.invoke(it) }

View file

@ -13,12 +13,13 @@ class TodosRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel) :
getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) }, getContentView(parent), { task, direction -> taskScoreEvents?.invoke(task, direction) },
{ task, item -> checklistItemScoreEvents?.invoke(task, item) }, { task, item -> checklistItemScoreEvents?.invoke(task, item) },
{ {
task -> task ->
taskOpenEvents?.invoke(task.first, task.second) taskOpenEvents?.invoke(task.first, task.second)
}, { }, {
task -> task ->
brokenTaskEvents?.invoke(task) brokenTaskEvents?.invoke(task)
}, viewModel) }, viewModel
)
} else { } else {
super.onCreateViewHolder(parent, viewType) super.onCreateViewHolder(parent, viewType)
} }

View file

@ -97,9 +97,11 @@ class AchievementsFragment : BaseMainFragment<FragmentRefreshRecyclerviewBinding
lifecycleScope.launch(ExceptionHandler.coroutine()) { lifecycleScope.launch(ExceptionHandler.coroutine()) {
userRepository.getAchievements().combine(userRepository.getQuestAchievements()) { achievements, questAchievements -> userRepository.getAchievements().combine(userRepository.getQuestAchievements()) { achievements, questAchievements ->
return@combine Pair(achievements, questAchievements) return@combine Pair(achievements, questAchievements)
}.combine(userRepository.getQuestAchievements() }.combine(
.map { it.mapNotNull { achievement -> achievement.questKey } } userRepository.getQuestAchievements()
.map { inventoryRepository.getQuestContent(it).firstOrNull() }) { achievements, content -> .map { it.mapNotNull { achievement -> achievement.questKey } }
.map { inventoryRepository.getQuestContent(it).firstOrNull() }
) { achievements, content ->
Pair(achievements, content) Pair(achievements, content)
}.collect { }.collect {
val achievements = it.first.first val achievements = it.first.first

View file

@ -59,7 +59,7 @@ abstract class BaseMainFragment<VB : ViewBinding> : BaseFragment<VB>() {
updateTabLayoutVisibility() updateTabLayoutVisibility()
updateToolbarInteractivity() updateToolbarInteractivity()
if (hidesToolbar) { if (hidesToolbar) {
hideToolbar() hideToolbar()
disableToolbarScrolling() disableToolbarScrolling()

View file

@ -141,14 +141,14 @@ class NavigationDrawerFragment : DialogFragment() {
initializeMenuItems() initializeMenuItems()
adapter.itemSelectedEvents = { adapter.itemSelectedEvents = {
setSelection(it.transitionId, it.bundle, true) setSelection(it.transitionId, it.bundle, true)
} }
adapter.promoClosedSubject = { adapter.promoClosedSubject = {
sharedPreferences.edit { sharedPreferences.edit {
putBoolean("hide$it", true) putBoolean("hide$it", true)
}
updatePromo()
} }
updatePromo()
}
lifecycleScope.launchCatching { lifecycleScope.launchCatching {
contentRepository.getWorldState() contentRepository.getWorldState()
@ -285,8 +285,10 @@ class NavigationDrawerFragment : DialogFragment() {
if (!user.hasClass && !hasSpecialItems) { if (!user.hasClass && !hasSpecialItems) {
item.isVisible = false item.isVisible = false
} else { } else {
if ((user.stats?.lvl if ((
?: 0) < HabiticaSnackbar.MIN_LEVEL_FOR_SKILLS && (!hasSpecialItems) user.stats?.lvl
?: 0
) < HabiticaSnackbar.MIN_LEVEL_FOR_SKILLS && (!hasSpecialItems)
) { ) {
item.pillText = getString(R.string.unlock_lvl_11) item.pillText = getString(R.string.unlock_lvl_11)
} else { } else {

View file

@ -40,7 +40,7 @@ class NewsFragment : BaseMainFragment<FragmentNewsBinding>() {
if (url?.contains("/static/new-stuff") == true) { if (url?.contains("/static/new-stuff") == true) {
view?.loadUrl(url) view?.loadUrl(url)
} else if (url != null) { } else if (url != null) {
MainNavigationController.navigate(url) MainNavigationController.navigate(url)
} }
return true return true
} }

View file

@ -80,22 +80,22 @@ class AvatarCustomizationFragment :
savedInstanceState: Bundle? savedInstanceState: Bundle?
): View? { ): View? {
showsBackButton = true showsBackButton = true
adapter.onCustomizationSelected = { customization -> adapter.onCustomizationSelected = { customization ->
lifecycleScope.launchCatching { lifecycleScope.launchCatching {
if (customization.identifier?.isNotBlank() != true) { if (customization.identifier?.isNotBlank() != true) {
userRepository.useCustomization(customization.type ?: "", customization.category, activeCustomization ?: "") userRepository.useCustomization(customization.type ?: "", customization.category, activeCustomization ?: "")
} else if (customization.type == "background" && ownedCustomizations.value.firstOrNull { it.key == customization.identifier } == null) { } else if (customization.type == "background" && ownedCustomizations.value.firstOrNull { it.key == customization.identifier } == null) {
userRepository.unlockPath(customization) userRepository.unlockPath(customization)
userRepository.retrieveUser(false, true, true) userRepository.retrieveUser(false, true, true)
} else { } else {
userRepository.useCustomization( userRepository.useCustomization(
customization.type ?: "", customization.type ?: "",
customization.category, customization.category,
customization.identifier ?: "" customization.identifier ?: ""
) )
}
} }
} }
}
lifecycleScope.launchCatching { lifecycleScope.launchCatching {
inventoryRepository.getInAppRewards() inventoryRepository.getInAppRewards()
@ -195,31 +195,31 @@ class AvatarCustomizationFragment :
.combine(currentFilter) { customizations, filter -> Pair(customizations, filter) } .combine(currentFilter) { customizations, filter -> Pair(customizations, filter) }
.combine(ownedCustomizations) { pair, ownedCustomizations -> Triple(pair.first, pair.second, ownedCustomizations) } .combine(ownedCustomizations) { pair, ownedCustomizations -> Triple(pair.first, pair.second, ownedCustomizations) }
.collect { (customizations, filter, ownedCustomizations) -> .collect { (customizations, filter, ownedCustomizations) ->
adapter.ownedCustomizations = adapter.ownedCustomizations =
ownedCustomizations.map { it.key + "_" + it.type + "_" + it.category } ownedCustomizations.map { it.key + "_" + it.type + "_" + it.category }
if (filter.isFiltering) { if (filter.isFiltering) {
val displayedCustomizations = mutableListOf<Customization>() val displayedCustomizations = mutableListOf<Customization>()
for (customization in customizations) { for (customization in customizations) {
if (shouldSkip(filter, ownedCustomizations, customization)) continue if (shouldSkip(filter, ownedCustomizations, customization)) continue
displayedCustomizations.add(customization) displayedCustomizations.add(customization)
}
adapter.setCustomizations(
if (!filter.ascending) {
displayedCustomizations.reversed()
} else {
displayedCustomizations
}
)
} else {
adapter.setCustomizations(
if (!filter.ascending) {
customizations.reversed()
} else {
customizations
}
)
} }
adapter.setCustomizations(
if (!filter.ascending) {
displayedCustomizations.reversed()
} else {
displayedCustomizations
}
)
} else {
adapter.setCustomizations(
if (!filter.ascending) {
customizations.reversed()
} else {
customizations
}
)
} }
}
} }
if (type == "hair" && (category == "beard" || category == "mustache")) { if (type == "hair" && (category == "beard" || category == "mustache")) {
val otherCategory = if (category == "mustache") "beard" else "mustache" val otherCategory = if (category == "mustache") "beard" else "mustache"

View file

@ -148,4 +148,4 @@ class AvatarEquipmentFragment :
binding?.refreshLayout?.isRefreshing = false binding?.refreshLayout?.isRefreshing = false
} }
} }
} }

View file

@ -39,7 +39,8 @@ import com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import javax.inject.Inject import javax.inject.Inject
open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBinding>(), open class AvatarOverviewFragment :
BaseMainFragment<FragmentComposeScrollingBinding>(),
AdapterView.OnItemSelectedListener { AdapterView.OnItemSelectedListener {
@Inject @Inject
@ -71,16 +72,18 @@ open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBin
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent { setContent {
HabiticaTheme { HabiticaTheme {
AvatarOverviewView(userViewModel, AvatarOverviewView(
userViewModel,
showCustomization, !showCustomization, showCustomization, !showCustomization,
battleGearWeapon.value?.twoHanded == true, costumeWeapon.value?.twoHanded == true, battleGearWeapon.value?.twoHanded == true, costumeWeapon.value?.twoHanded == true,
{ type, category -> { type, category ->
displayCustomizationFragment(type, category) displayCustomizationFragment(type, category)
}, { type, category -> }, { type, category ->
displayAvatarEquipmentFragment(type, category) displayAvatarEquipmentFragment(type, category)
}, { type, equipped, isCostume -> }, { type, equipped, isCostume ->
displayEquipmentFragment(type, equipped, isCostume) displayEquipmentFragment(type, equipped, isCostume)
}) }
)
} }
} }
} }
@ -137,7 +140,8 @@ open class AvatarOverviewFragment : BaseMainFragment<FragmentComposeScrollingBin
} }
@Composable @Composable
fun AvatarOverviewView(userViewModel: MainUserViewModel, fun AvatarOverviewView(
userViewModel: MainUserViewModel,
showCustomization: Boolean = true, showCustomization: Boolean = true,
showEquipment: Boolean = true, showEquipment: Boolean = true,
battleGearTwoHanded: Boolean = false, battleGearTwoHanded: Boolean = false,
@ -145,12 +149,13 @@ fun AvatarOverviewView(userViewModel: MainUserViewModel,
onCustomizationTap: (String, String?) -> Unit, onCustomizationTap: (String, String?) -> Unit,
onAvatarEquipmentTap: (String, String?) -> Unit, onAvatarEquipmentTap: (String, String?) -> Unit,
onEquipmentTap: (String, String?, Boolean) -> Unit onEquipmentTap: (String, String?, Boolean) -> Unit
) { ) {
val user by userViewModel.user.observeAsState() val user by userViewModel.user.observeAsState()
Column( Column(
Modifier Modifier
.padding(horizontal = 8.dp) .padding(horizontal = 8.dp)
.padding(bottom = 16.dp)) { .padding(bottom = 16.dp)
) {
if (showCustomization) { if (showCustomization) {
Row( Row(
Modifier.padding(horizontal = 12.dp, vertical = 15.dp), Modifier.padding(horizontal = 12.dp, vertical = 15.dp),
@ -162,18 +167,21 @@ fun AvatarOverviewView(userViewModel: MainUserViewModel,
color = HabiticaTheme.colors.textSecondary color = HabiticaTheme.colors.textSecondary
) )
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
SegmentedControl(items = listOf( SegmentedControl(
stringResource(R.string.avatar_size_slim), stringResource( items = listOf(
R.string.avatar_size_broad stringResource(R.string.avatar_size_slim),
) stringResource(
), R.string.avatar_size_broad
)
),
defaultSelectedItemIndex = if (user?.preferences?.size == "slim") 0 else 1, defaultSelectedItemIndex = if (user?.preferences?.size == "slim") 0 else 1,
onItemSelection = { onItemSelection = {
userViewModel.updateUser( userViewModel.updateUser(
"preferences.size", "preferences.size",
if (it == 0) "slim" else "broad" if (it == 0) "slim" else "broad"
) )
}) }
)
} }
AvatarCustomizationOverviewView(user?.preferences, user?.items?.gear?.equipped, onCustomizationTap, onAvatarEquipmentTap) AvatarCustomizationOverviewView(user?.preferences, user?.items?.gear?.equipped, onCustomizationTap, onAvatarEquipmentTap)
} }
@ -229,4 +237,4 @@ fun AvatarOverviewView(userViewModel: MainUserViewModel,
} }
} }
} }
} }

View file

@ -7,4 +7,4 @@ class EquipmentOverviewFragment : AvatarOverviewFragment() {
showCustomization = false showCustomization = false
super.onCreate(savedInstanceState) super.onCreate(savedInstanceState)
} }
} }

View file

@ -269,37 +269,36 @@ class ItemDialogFragment : BaseDialogFragment<FragmentItemsDialogBinding>() {
else -> Egg::class.java else -> Egg::class.java
} }
itemType?.let { type -> itemType?.let { type ->
lifecycleScope.launch(ExceptionHandler.coroutine()) { lifecycleScope.launch(ExceptionHandler.coroutine()) {
inventoryRepository.getOwnedItems(type) inventoryRepository.getOwnedItems(type)
.onEach { items -> .onEach { items ->
val filteredItems = if (isFeeding) { val filteredItems = if (isFeeding) {
items.filter { it.key != "Saddle" } items.filter { it.key != "Saddle" }
} else { } else {
items items
}
adapter?.data = filteredItems
} }
.map { items -> items.mapNotNull { it.key } } adapter?.data = filteredItems
.map { inventoryRepository.getItems(itemClass, it.toTypedArray()).firstOrNull() } }
.collect { .map { items -> items.mapNotNull { it.key } }
val itemMap = mutableMapOf<String, Item>() .map { inventoryRepository.getItems(itemClass, it.toTypedArray()).firstOrNull() }
for (item in it ?: emptyList()) { .collect {
itemMap[item.key] = item val itemMap = mutableMapOf<String, Item>()
} for (item in it ?: emptyList()) {
adapter?.items = itemMap itemMap[item.key] = item
} }
adapter?.items = itemMap
} }
lifecycleScope.launch(ExceptionHandler.coroutine()) { }
inventoryRepository.getPets().collect { adapter?.setExistingPets(it) } lifecycleScope.launch(ExceptionHandler.coroutine()) {
} inventoryRepository.getPets().collect { adapter?.setExistingPets(it) }
lifecycleScope.launch(ExceptionHandler.coroutine()) { }
inventoryRepository.getOwnedPets().map { ownedPets -> lifecycleScope.launch(ExceptionHandler.coroutine()) {
val petMap = mutableMapOf<String, OwnedPet>() inventoryRepository.getOwnedPets().map { ownedPets ->
ownedPets.forEach { petMap[it.key ?: ""] = it } val petMap = mutableMapOf<String, OwnedPet>()
return@map petMap ownedPets.forEach { petMap[it.key ?: ""] = it }
}.collect { adapter?.setOwnedPets(it) } return@map petMap
} }.collect { adapter?.setOwnedPets(it) }
}
} }
} }

View file

@ -225,7 +225,7 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
alert?.setMessage(R.string.quest_party_required_description) alert?.setMessage(R.string.quest_party_required_description)
alert?.addButton(R.string.create_new_party, true, false) { _, _ -> alert?.addButton(R.string.create_new_party, true, false) { _, _ ->
lifecycleScope.launch(ExceptionHandler.coroutine()) { lifecycleScope.launch(ExceptionHandler.coroutine()) {
val group = socialRepository.createGroup( socialRepository.createGroup(
getString(R.string.usernames_party, user?.profile?.name), getString(R.string.usernames_party, user?.profile?.name),
"", "",
user?.id, user?.id,

View file

@ -276,7 +276,7 @@ open class ShopFragment : BaseMainFragment<FragmentRefreshRecyclerviewBinding>()
lifecycleScope.launchCatching { lifecycleScope.launchCatching {
val shop = inventoryRepository.retrieveMarketGear() val shop = inventoryRepository.retrieveMarketGear()
inventoryRepository.getOwnedEquipment() inventoryRepository.getOwnedEquipment()
.map { equipment -> equipment.map { it.key } } .map { equipment -> equipment.map { it.key } }
.collect { equipment -> .collect { equipment ->
for (category in shop?.categories ?: emptyList()) { for (category in shop?.categories ?: emptyList()) {
val items = category.items.asSequence().filter { val items = category.items.asSequence().filter {

View file

@ -138,7 +138,7 @@ class MountDetailRecyclerFragment :
if (animalType != null || animalGroup != null) { if (animalType != null || animalGroup != null) {
lifecycleScope.launch(ExceptionHandler.coroutine()) { lifecycleScope.launch(ExceptionHandler.coroutine()) {
val mounts = inventoryRepository.getMounts(animalType, animalGroup, animalColor).firstOrNull() ?: emptyList() val mounts = inventoryRepository.getMounts(animalType, animalGroup, animalColor).firstOrNull() ?: emptyList()
inventoryRepository.getOwnedMounts().map { ownedMounts -> inventoryRepository.getOwnedMounts().map { ownedMounts ->
val mountMap = mutableMapOf<String, OwnedMount>() val mountMap = mutableMapOf<String, OwnedMount>()
ownedMounts.forEach { mountMap[it.key ?: ""] = it } ownedMounts.forEach { mountMap[it.key ?: ""] = it }
return@map mountMap return@map mountMap

View file

@ -218,7 +218,6 @@ class PetDetailRecyclerFragment :
} }
adapter.setItemList(items) adapter.setItemList(items)
} }
} }
} }
} }

View file

@ -172,8 +172,6 @@ class StableRecyclerFragment :
} }
} }
companion object { companion object {
private const val ITEM_TYPE_KEY = "CLASS_TYPE_KEY" private const val ITEM_TYPE_KEY = "CLASS_TYPE_KEY"
private const val HEADER_VIEW_TYPE = 0 private const val HEADER_VIEW_TYPE = 0

View file

@ -24,8 +24,6 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.ApiClient import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.extensions.addCancelButton import com.habitrpg.android.habitica.extensions.addCancelButton
import com.habitrpg.android.habitica.extensions.addCloseButton import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.layoutInflater
import com.habitrpg.android.habitica.helpers.ExceptionHandler import com.habitrpg.android.habitica.helpers.ExceptionHandler
import com.habitrpg.android.habitica.helpers.MainNavigationController import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.launchCatching import com.habitrpg.android.habitica.helpers.launchCatching
@ -41,6 +39,8 @@ import com.habitrpg.android.habitica.ui.views.ValidatingEditText
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaProgressDialog import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaProgressDialog
import com.habitrpg.common.habitica.api.HostConfig import com.habitrpg.common.habitica.api.HostConfig
import com.habitrpg.common.habitica.extensions.dpToPx
import com.habitrpg.common.habitica.extensions.layoutInflater
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import retrofit2.HttpException import retrofit2.HttpException
import javax.inject.Inject import javax.inject.Inject
@ -48,7 +48,7 @@ import javax.inject.Inject
class AccountPreferenceFragment : class AccountPreferenceFragment :
BasePreferencesFragment(), BasePreferencesFragment(),
SharedPreferences.OnSharedPreferenceChangeListener, SharedPreferences.OnSharedPreferenceChangeListener,
AccountUpdateConfirmed { AccountUpdateConfirmed {
@Inject @Inject
lateinit var hostConfig: HostConfig lateinit var hostConfig: HostConfig
@Inject @Inject
@ -334,7 +334,7 @@ class AccountPreferenceFragment :
user?.username ?: "", user?.username ?: "",
email ?: "", email ?: "",
passwordEditText.text ?: "", passwordEditText.text ?: "",
passwordRepeatEditText?.text ?: "" passwordRepeatEditText.text ?: ""
) )
(activity as? SnackbarActivity)?.showSnackbar( (activity as? SnackbarActivity)?.showSnackbar(
content = context.getString(R.string.password_added), content = context.getString(R.string.password_added),
@ -454,10 +454,10 @@ class AccountPreferenceFragment :
ExceptionHandler.reportError(throwable) ExceptionHandler.reportError(throwable)
}) { }) {
userRepository.deleteAccount(password) userRepository.deleteAccount(password)
dialog?.dismiss() dialog?.dismiss()
accountDialog.dismiss() accountDialog.dismiss()
context?.let { HabiticaBaseApplication.logout(it) } context?.let { HabiticaBaseApplication.logout(it) }
activity?.finish() activity?.finish()
} }
} }
@ -471,7 +471,6 @@ class AccountPreferenceFragment :
if (habiticaAccountDialog != null) { if (habiticaAccountDialog != null) {
accountDialog = habiticaAccountDialog accountDialog = habiticaAccountDialog
} }
} }
private fun showConfirmUsernameDialog() { private fun showConfirmUsernameDialog() {
@ -519,5 +518,4 @@ class AccountPreferenceFragment :
override fun deletionConfirmClicked(confirmationString: String) { override fun deletionConfirmClicked(confirmationString: String) {
deleteAccount(confirmationString) deleteAccount(confirmationString)
} }
} }

View file

@ -15,7 +15,6 @@ import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DialogHabiticaAccountBinding import com.habitrpg.android.habitica.databinding.DialogHabiticaAccountBinding
import com.habitrpg.android.habitica.models.user.User import com.habitrpg.android.habitica.models.user.User
class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R.layout.dialog_habitica_account) { class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R.layout.dialog_habitica_account) {
private var _binding: DialogHabiticaAccountBinding? = null private var _binding: DialogHabiticaAccountBinding? = null
private val binding get() = _binding!! private val binding get() = _binding!!
@ -46,7 +45,7 @@ class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R
"delete_account" -> setDeleteAccountViews() "delete_account" -> setDeleteAccountViews()
} }
binding.backImagebutton.setOnClickListener{dismiss()} binding.backImagebutton.setOnClickListener { dismiss() }
} }
private fun setResetAccountViews() { private fun setResetAccountViews() {
@ -103,7 +102,8 @@ class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R
override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) { override fun onTextChanged(p0: CharSequence?, p1: Int, p2: Int, p3: Int) {
if (binding.confirmationInputEdittext.text.toString().isNotEmpty()) { if (binding.confirmationInputEdittext.text.toString().isNotEmpty()) {
if ((user?.authentication?.hasPassword != true && binding.confirmationInputEdittext.text.toString() == context?.getString(R.string.delete_caps)) || if ((user?.authentication?.hasPassword != true && binding.confirmationInputEdittext.text.toString() == context?.getString(R.string.delete_caps)) ||
user?.authentication?.hasPassword == true) { user?.authentication?.hasPassword == true
) {
binding.confirmActionTextview.setTextColor(ContextCompat.getColor(thisContext, R.color.red_100)) binding.confirmActionTextview.setTextColor(ContextCompat.getColor(thisContext, R.color.red_100))
binding.confirmActionTextview.alpha = 1.0f binding.confirmActionTextview.alpha = 1.0f
} }
@ -127,7 +127,6 @@ class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R
accountUpdateConfirmed?.deletionConfirmClicked(confirmationString) accountUpdateConfirmed?.deletionConfirmClicked(confirmationString)
} }
} }
} }
} }
@ -135,10 +134,8 @@ class HabiticaAccountDialog(private var thisContext: Context) : DialogFragment(R
return R.style.HabiticaAccountDialogTheme return R.style.HabiticaAccountDialogTheme
} }
interface AccountUpdateConfirmed { interface AccountUpdateConfirmed {
fun resetConfirmedClicked() fun resetConfirmedClicked()
fun deletionConfirmClicked(confirmationString: String) fun deletionConfirmClicked(confirmationString: String)
} }
} }

Some files were not shown because too many files have changed in this diff Show more