mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 19:56:32 +00:00
Fix linting errors
# Conflicts: # Habitica/src/main/java/com/habitrpg/android/habitica/helpers/Analytics.kt # Habitica/src/main/java/com/habitrpg/android/habitica/helpers/PurchaseHandler.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/ClassSelectionActivity.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/NotificationsActivity.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/preferences/PreferencesFragment.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/support/FAQOverviewFragment.kt # Habitica/src/main/java/com/habitrpg/android/habitica/ui/views/AppHeaderView.kt # common/src/main/java/com/habitrpg/common/habitica/helpers/MarkdownParser.kt # common/src/main/java/com/habitrpg/common/habitica/helpers/NumberAbbreviator.kt
This commit is contained in:
parent
2343ebf5ed
commit
7d230ecfa1
294 changed files with 1881 additions and 1083 deletions
|
|
@ -26,6 +26,7 @@ class MainActivityTest : ActivityTestCase() {
|
|||
val screen = MainActivityScreen()
|
||||
|
||||
lateinit var scenario: ActivityScenario<MainActivity>
|
||||
|
||||
@After
|
||||
fun cleanup() {
|
||||
scenario.close()
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import io.github.kakaocup.kakao.toolbar.KToolbar
|
|||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.justRun
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.slot
|
||||
import io.mockk.verify
|
||||
|
|
|
|||
|
|
@ -26,9 +26,9 @@ class NavigationDrawerScreen : Screen<NavigationDrawerScreen>() {
|
|||
val recycler: KRecyclerView = KRecyclerView({
|
||||
withId(R.id.recyclerView)
|
||||
}, itemTypeBuilder = {
|
||||
itemType(::SectionHeaderItem)
|
||||
itemType(::MainItem)
|
||||
})
|
||||
itemType(::SectionHeaderItem)
|
||||
itemType(::MainItem)
|
||||
})
|
||||
}
|
||||
|
||||
@LargeTest
|
||||
|
|
|
|||
|
|
@ -6,7 +6,6 @@ import android.widget.TextView
|
|||
import androidx.fragment.app.testing.launchFragmentInContainer
|
||||
import androidx.test.espresso.UiController
|
||||
import androidx.test.espresso.ViewAction
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
|
||||
import com.habitrpg.android.habitica.interactors.HatchPetUseCase
|
||||
|
|
@ -25,7 +24,6 @@ import io.mockk.coEvery
|
|||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.hamcrest.Matcher
|
||||
|
|
@ -63,8 +61,8 @@ class ItemScreen : Screen<ItemScreen>() {
|
|||
val recycler: KRecyclerView = KRecyclerView({
|
||||
withId(R.id.recyclerView)
|
||||
}, itemTypeBuilder = {
|
||||
itemType(::ItemItem)
|
||||
})
|
||||
itemType(::ItemItem)
|
||||
})
|
||||
}
|
||||
|
||||
internal class ItemRecyclerFragmentTest : FragmentTestCase<ItemRecyclerFragment, FragmentRecyclerviewBinding, ItemScreen>(false) {
|
||||
|
|
|
|||
|
|
@ -2,13 +2,11 @@ package com.habitrpg.android.habitica.ui.fragments.inventory.stable
|
|||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.testing.launchFragmentInContainer
|
||||
import com.habitrpg.android.habitica.HabiticaBaseApplication
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRecyclerviewBinding
|
||||
import com.habitrpg.android.habitica.interactors.FeedPetUseCase
|
||||
import com.habitrpg.android.habitica.models.user.OwnedItem
|
||||
import com.habitrpg.android.habitica.ui.fragments.FragmentTestCase
|
||||
import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemDialogFragment
|
||||
import io.github.kakaocup.kakao.common.views.KView
|
||||
import io.github.kakaocup.kakao.recycler.KRecyclerView
|
||||
import io.github.kakaocup.kakao.screen.Screen
|
||||
|
|
@ -18,7 +16,6 @@ import io.mockk.coEvery
|
|||
import io.mockk.coVerify
|
||||
import io.mockk.every
|
||||
import io.mockk.mockk
|
||||
import io.mockk.mockkObject
|
||||
import io.mockk.spyk
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Test
|
||||
|
|
@ -27,9 +24,9 @@ class PetDetailScreen : Screen<PetDetailScreen>() {
|
|||
val recycler: KRecyclerView = KRecyclerView({
|
||||
withId(R.id.recyclerView)
|
||||
}, itemTypeBuilder = {
|
||||
itemType(::SectionItem)
|
||||
itemType(::PetItem)
|
||||
})
|
||||
itemType(::SectionItem)
|
||||
itemType(::PetItem)
|
||||
})
|
||||
}
|
||||
|
||||
internal class PetDetailRecyclerFragmentTest :
|
||||
|
|
|
|||
|
|
@ -33,9 +33,9 @@ class StableScreen : Screen<StableScreen>() {
|
|||
val recycler: KRecyclerView = KRecyclerView({
|
||||
withId(R.id.recyclerView)
|
||||
}, itemTypeBuilder = {
|
||||
itemType(::SectionItem)
|
||||
itemType(::PetItem)
|
||||
})
|
||||
itemType(::SectionItem)
|
||||
itemType(::PetItem)
|
||||
})
|
||||
}
|
||||
|
||||
internal class StableRecyclerFragmentTest : FragmentTestCase<StableRecyclerFragment, FragmentRecyclerviewBinding, StableScreen>(false) {
|
||||
|
|
|
|||
|
|
@ -31,8 +31,8 @@ class TaskListScreen : Screen<TaskListScreen>() {
|
|||
val recycler: KRecyclerView = KRecyclerView({
|
||||
withId(R.id.recyclerView)
|
||||
}, itemTypeBuilder = {
|
||||
itemType(::TaskItem)
|
||||
})
|
||||
itemType(::TaskItem)
|
||||
})
|
||||
}
|
||||
|
||||
internal class TaskRecyclerViewFragmentTest : FragmentTestCase<TaskRecyclerViewFragment, FragmentRefreshRecyclerviewBinding, TaskListScreen>(false) {
|
||||
|
|
|
|||
|
|
@ -48,14 +48,19 @@ import javax.inject.Inject
|
|||
abstract class HabiticaBaseApplication : Application(), Application.ActivityLifecycleCallbacks {
|
||||
@Inject
|
||||
internal lateinit var lazyApiHelper: ApiClient
|
||||
|
||||
@Inject
|
||||
internal lateinit var sharedPrefs: SharedPreferences
|
||||
|
||||
@Inject
|
||||
internal lateinit var analyticsManager: AnalyticsManager
|
||||
|
||||
@Inject
|
||||
internal lateinit var pushNotificationManager: PushNotificationManager
|
||||
|
||||
@Inject
|
||||
internal lateinit var authenticationHandler: AuthenticationHandler
|
||||
|
||||
/**
|
||||
* For better performance billing class should be used as singleton
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ interface ApiService {
|
|||
|
||||
@GET("inbox/messages")
|
||||
suspend fun getInboxMessages(@Query("conversation") uuid: String, @Query("page") page: Int): HabitResponse<List<ChatMessage>>
|
||||
|
||||
@GET("inbox/conversations")
|
||||
suspend fun getInboxConversations(): HabitResponse<List<InboxConversation>>
|
||||
|
||||
|
|
@ -128,6 +129,7 @@ interface ApiService {
|
|||
|
||||
@POST("tasks/{id}/score/{direction}")
|
||||
suspend fun postTaskDirection(@Path("id") id: String, @Path("direction") direction: String): HabitResponse<TaskDirectionData>
|
||||
|
||||
@POST("tasks/bulk-score")
|
||||
suspend fun bulkScoreTasks(@Body data: List<Map<String, String>>): HabitResponse<BulkTaskScoringData>
|
||||
|
||||
|
|
@ -139,6 +141,7 @@ interface ApiService {
|
|||
|
||||
@POST("tasks/user")
|
||||
suspend fun createTask(@Body item: Task): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/group/{groupId}")
|
||||
suspend fun createGroupTask(@Path("groupId") groupId: String, @Body item: Task): HabitResponse<Task>
|
||||
|
||||
|
|
@ -287,8 +290,10 @@ interface ApiService {
|
|||
suspend fun inviteToQuest(@Path("gid") groupId: String, @Path("questKey") questKey: String): HabitResponse<Quest>
|
||||
|
||||
@GET("groups/{gid}/invites")
|
||||
suspend fun getGroupInvites(@Path("gid") groupId: String,
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?): HabitResponse<List<Member>>
|
||||
suspend fun getGroupInvites(
|
||||
@Path("gid") groupId: String,
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?
|
||||
): HabitResponse<List<Member>>
|
||||
|
||||
@POST("groups/{gid}/quests/abort")
|
||||
suspend fun abortQuest(@Path("gid") groupId: String): HabitResponse<Quest>
|
||||
|
|
|
|||
|
|
@ -277,6 +277,6 @@ interface ApiClient {
|
|||
suspend fun updateMember(memberID: String, updateData: Map<String, Any?>): Member?
|
||||
suspend fun getHallMember(userId: String): Member?
|
||||
suspend fun markTaskNeedsWork(taskID: String, userID: String): Task?
|
||||
suspend fun retrievePartySeekingUsers(page: Int) : List<Member>?
|
||||
suspend fun retrievePartySeekingUsers(page: Int): List<Member>?
|
||||
suspend fun getGroupInvites(groupId: String, includeAllPublicFields: Boolean?): List<Member>?
|
||||
}
|
||||
|
|
|
|||
|
|
@ -21,7 +21,7 @@ interface UserRepository : BaseRepository {
|
|||
fun getUser(userID: String): Flow<User?>
|
||||
|
||||
suspend fun updateUser(updateData: Map<String, Any?>): User?
|
||||
suspend fun updateUser(key : String, value : Any?): User?
|
||||
suspend fun updateUser(key: String, value: Any?): User?
|
||||
|
||||
suspend fun retrieveUser(withTasks: Boolean = false, forced: Boolean = false, overrideExisting: Boolean = false): User?
|
||||
|
||||
|
|
|
|||
|
|
@ -378,7 +378,9 @@ class ApiClientImpl(
|
|||
override suspend fun validateSubscription(request: PurchaseValidationRequest): Any? {
|
||||
return if (lastSubscribeCall == null || Date().time - lastSubscribeCall.time > 60000) {
|
||||
process { apiService.validateSubscription(request) }
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getHallMember(userId: String): Member? {
|
||||
|
|
@ -629,7 +631,9 @@ class ApiClientImpl(
|
|||
// make sure a purchase attempt doesn't happen
|
||||
return if (lastPurchaseValidation == null || Date().time - lastPurchaseValidation.time > 5000) {
|
||||
return process { apiService.validatePurchase(request) }
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun changeCustomDayStart(updateObject: Map<String, Any>): User? {
|
||||
|
|
@ -640,7 +644,7 @@ class ApiClientImpl(
|
|||
return process { apiService.markTaskNeedsWork(taskID, userID) }
|
||||
}
|
||||
|
||||
override suspend fun retrievePartySeekingUsers(page: Int) : List<Member>? {
|
||||
override suspend fun retrievePartySeekingUsers(page: Int): List<Member>? {
|
||||
return process { apiService.retrievePartySeekingUsers(page) }
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -9,10 +9,10 @@ import com.habitrpg.android.habitica.modules.AuthenticationHandler
|
|||
abstract class BaseRepositoryImpl<T : BaseLocalRepository>(
|
||||
protected val localRepository: T,
|
||||
protected val apiClient: ApiClient,
|
||||
protected val authenticationHandler : AuthenticationHandler
|
||||
protected val authenticationHandler: AuthenticationHandler
|
||||
) : BaseRepository {
|
||||
|
||||
val currentUserID : String
|
||||
val currentUserID: String
|
||||
get() = authenticationHandler.currentUserID ?: ""
|
||||
|
||||
override fun close() {
|
||||
|
|
|
|||
|
|
@ -8,16 +8,16 @@ import com.habitrpg.android.habitica.modules.AuthenticationHandler
|
|||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
class FAQRepositoryImpl(
|
||||
localRepository : FAQLocalRepository,
|
||||
apiClient : ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
localRepository: FAQLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler: AuthenticationHandler
|
||||
) : BaseRepositoryImpl<FAQLocalRepository>(localRepository, apiClient, authenticationHandler),
|
||||
FAQRepository {
|
||||
override fun getArticle(position : Int) : Flow<FAQArticle> {
|
||||
override fun getArticle(position: Int): Flow<FAQArticle> {
|
||||
return localRepository.getArticle(position)
|
||||
}
|
||||
|
||||
override fun getArticles() : Flow<List<FAQArticle>> {
|
||||
override fun getArticles(): Flow<List<FAQArticle>> {
|
||||
return localRepository.articles
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,7 @@ class InventoryRepositoryImpl(
|
|||
return localRepository.getInAppRewards()
|
||||
}
|
||||
|
||||
override fun getInAppReward(key : String) : Flow<ShopItem> {
|
||||
override fun getInAppReward(key: String): Flow<ShopItem> {
|
||||
return localRepository.getInAppReward(key)
|
||||
}
|
||||
|
||||
|
|
@ -120,7 +120,7 @@ class InventoryRepositoryImpl(
|
|||
}
|
||||
|
||||
override fun getOwnedMounts(): Flow<List<OwnedMount>> {
|
||||
return authenticationHandler.userIDFlow.flatMapLatest { localRepository.getOwnedMounts(it) }
|
||||
return authenticationHandler.userIDFlow.flatMapLatest { localRepository.getOwnedMounts(it) }
|
||||
}
|
||||
|
||||
override fun getPets(): Flow<List<Pet>> {
|
||||
|
|
@ -132,7 +132,7 @@ class InventoryRepositoryImpl(
|
|||
}
|
||||
|
||||
override fun getOwnedPets(): Flow<List<OwnedPet>> {
|
||||
return authenticationHandler.userIDFlow.flatMapLatest { localRepository.getOwnedPets(it) }
|
||||
return authenticationHandler.userIDFlow.flatMapLatest { localRepository.getOwnedPets(it) }
|
||||
}
|
||||
|
||||
override fun updateOwnedEquipment(user: User) {
|
||||
|
|
|
|||
|
|
@ -53,11 +53,11 @@ class SocialRepositoryImpl(
|
|||
return apiClient.updateMember(memberID, mapOf(key to value))
|
||||
}
|
||||
|
||||
override suspend fun retrievePartySeekingUsers(page: Int) : List<Member>? {
|
||||
override suspend fun retrievePartySeekingUsers(page: Int): List<Member>? {
|
||||
return apiClient.retrievePartySeekingUsers(page)
|
||||
}
|
||||
|
||||
override fun getGroupMembership(id: String) = authenticationHandler.userIDFlow.flatMapLatest { localRepository.getGroupMembership(it, id) }
|
||||
override fun getGroupMembership(id: String) = authenticationHandler.userIDFlow.flatMapLatest { localRepository.getGroupMembership(it, id) }
|
||||
|
||||
override fun getGroupMemberships(): Flow<List<GroupMembership>> {
|
||||
return authenticationHandler.userIDFlow.flatMapLatest { localRepository.getGroupMemberships(it) }
|
||||
|
|
@ -277,7 +277,6 @@ class SocialRepositoryImpl(
|
|||
|
||||
override suspend fun retrievegroupInvites(id: String, includeAllPublicFields: Boolean) = apiClient.getGroupInvites(id, includeAllPublicFields)
|
||||
|
||||
|
||||
override suspend fun retrieveMemberWithUsername(username: String?, fromHall: Boolean): Member? {
|
||||
return retrieveMember(username, fromHall)
|
||||
}
|
||||
|
|
@ -363,7 +362,9 @@ class SocialRepositoryImpl(
|
|||
override suspend fun getMemberAchievements(userId: String?): List<Achievement>? {
|
||||
return if (userId == null) {
|
||||
null
|
||||
} else apiClient.getMemberAchievements(userId)
|
||||
} else {
|
||||
apiClient.getMemberAchievements(userId)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun transferGems(giftedID: String, amount: Int): Void? {
|
||||
|
|
|
|||
|
|
@ -12,51 +12,51 @@ import kotlinx.coroutines.flow.Flow
|
|||
import kotlinx.coroutines.flow.flatMapLatest
|
||||
|
||||
class TagRepositoryImpl(
|
||||
localRepository : TagLocalRepository,
|
||||
apiClient : ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
localRepository: TagLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler: AuthenticationHandler
|
||||
) : BaseRepositoryImpl<TagLocalRepository>(localRepository, apiClient, authenticationHandler),
|
||||
TagRepository {
|
||||
|
||||
override fun getTags() = authenticationHandler.userIDFlow.flatMapLatest { getTags(it) }
|
||||
|
||||
override fun getTags(userId : String) : Flow<List<Tag>> {
|
||||
override fun getTags(userId: String): Flow<List<Tag>> {
|
||||
return localRepository.getTags(userId)
|
||||
}
|
||||
|
||||
override suspend fun createTag(tag : Tag) : Tag? {
|
||||
override suspend fun createTag(tag: Tag): Tag? {
|
||||
val savedTag = apiClient.createTag(tag) ?: return null
|
||||
savedTag.userId = currentUserID
|
||||
localRepository.save(savedTag)
|
||||
return savedTag
|
||||
}
|
||||
|
||||
override suspend fun updateTag(tag : Tag) : Tag? {
|
||||
override suspend fun updateTag(tag: Tag): Tag? {
|
||||
val savedTag = apiClient.updateTag(tag.id, tag) ?: return null
|
||||
savedTag.userId = currentUserID
|
||||
localRepository.save(savedTag)
|
||||
return savedTag
|
||||
}
|
||||
|
||||
override suspend fun deleteTag(id : String) : Void? {
|
||||
override suspend fun deleteTag(id: String): Void? {
|
||||
apiClient.deleteTag(id)
|
||||
localRepository.deleteTag(id)
|
||||
return null
|
||||
}
|
||||
|
||||
override suspend fun createTags(tags : Collection<Tag>) : List<Tag> {
|
||||
override suspend fun createTags(tags: Collection<Tag>): List<Tag> {
|
||||
return tags.mapNotNull {
|
||||
createTag(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun updateTags(tags : Collection<Tag>) : List<Tag> {
|
||||
override suspend fun updateTags(tags: Collection<Tag>): List<Tag> {
|
||||
return tags.mapNotNull {
|
||||
updateTag(it)
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun deleteTags(tagIds : Collection<String>) : List<Void> {
|
||||
override suspend fun deleteTags(tagIds: Collection<String>): List<Void> {
|
||||
return tagIds.mapNotNull {
|
||||
deleteTag(it)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -36,7 +36,7 @@ import java.util.UUID
|
|||
class TaskRepositoryImpl(
|
||||
localRepository: TaskLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler,
|
||||
authenticationHandler: AuthenticationHandler,
|
||||
val appConfigManager: AppConfigManager,
|
||||
val analyticsManager: AnalyticsManager
|
||||
) : BaseRepositoryImpl<TaskLocalRepository>(localRepository, apiClient, authenticationHandler), TaskRepository {
|
||||
|
|
@ -79,7 +79,9 @@ class TaskRepositoryImpl(
|
|||
): TaskScoringResult? {
|
||||
val localData = if (user != null && appConfigManager.enableLocalTaskScoring()) {
|
||||
ScoreTaskLocallyInteractor.score(user, task, if (up) TaskDirection.UP else TaskDirection.DOWN)
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
if (user != null && localData != null) {
|
||||
val stats = user.stats
|
||||
val result = TaskScoringResult(localData, stats)
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ class UserRepositoryImpl(
|
|||
return mergeUser(oldUser, networkUser)
|
||||
}
|
||||
|
||||
private suspend fun updateUser(userID : String, key : String, value : Any?): User? {
|
||||
private suspend fun updateUser(userID: String, key: String, value: Any?): User? {
|
||||
return updateUser(userID, mapOf(key to value))
|
||||
}
|
||||
|
||||
|
|
@ -64,7 +64,7 @@ class UserRepositoryImpl(
|
|||
return updateUser(currentUserID, updateData)
|
||||
}
|
||||
|
||||
override suspend fun updateUser(key : String, value : Any?): User? {
|
||||
override suspend fun updateUser(key: String, value: Any?): User? {
|
||||
return updateUser(currentUserID, key, value)
|
||||
}
|
||||
|
||||
|
|
@ -382,9 +382,11 @@ class UserRepositoryImpl(
|
|||
taskRepository.saveTasks(id, tasksOrder, tasks)
|
||||
}
|
||||
val members = apiClient.getGroupMembers(teamID, true) ?: return team
|
||||
localRepository.save(members.map {
|
||||
GroupMembership(it.id, id)
|
||||
})
|
||||
localRepository.save(
|
||||
members.map {
|
||||
GroupMembership(it.id, id)
|
||||
}
|
||||
)
|
||||
members.let { localRepository.save(members) }
|
||||
return team
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,7 +29,8 @@ class RealmTaskLocalRepository(realm: Realm) : RealmBaseLocalRepository(realm),
|
|||
|
||||
private fun findTasks(
|
||||
taskType: TaskType,
|
||||
ownerID: String): RealmResults<Task> {
|
||||
ownerID: String
|
||||
): RealmResults<Task> {
|
||||
return realm.where(Task::class.java)
|
||||
.equalTo("typeValue", taskType.value)
|
||||
.equalTo("ownerID", ownerID)
|
||||
|
|
|
|||
|
|
@ -43,16 +43,24 @@ fun Long.getAgoString(res: Resources): String {
|
|||
return when {
|
||||
diffMonths != 0L -> if (diffMonths == 1L) {
|
||||
res.getString(R.string.ago_1month)
|
||||
} else res.getString(R.string.ago_months, diffMonths)
|
||||
} else {
|
||||
res.getString(R.string.ago_months, diffMonths)
|
||||
}
|
||||
diffWeeks != 0L -> if (diffWeeks == 1L) {
|
||||
res.getString(R.string.ago_1week)
|
||||
} else res.getString(R.string.ago_weeks, diffWeeks)
|
||||
} else {
|
||||
res.getString(R.string.ago_weeks, diffWeeks)
|
||||
}
|
||||
diffDays != 0L -> if (diffDays == 1L) {
|
||||
res.getString(R.string.ago_1day)
|
||||
} else res.getString(R.string.ago_days, diffDays)
|
||||
} else {
|
||||
res.getString(R.string.ago_days, diffDays)
|
||||
}
|
||||
diffHours != 0L -> if (diffHours == 1L) {
|
||||
res.getString(R.string.ago_1hour)
|
||||
} else res.getString(R.string.ago_hours, diffHours)
|
||||
} else {
|
||||
res.getString(R.string.ago_hours, diffHours)
|
||||
}
|
||||
diffMinutes == 1L -> res.getString(R.string.ago_1Minute)
|
||||
else -> res.getString(R.string.ago_minutes, diffMinutes)
|
||||
}
|
||||
|
|
@ -74,16 +82,24 @@ fun Long.getRemainingString(res: Resources): String {
|
|||
return when {
|
||||
diffMonths != 0L -> if (diffMonths == 1L) {
|
||||
res.getString(R.string.remaining_1month)
|
||||
} else res.getString(R.string.remaining_months, diffMonths)
|
||||
} else {
|
||||
res.getString(R.string.remaining_months, diffMonths)
|
||||
}
|
||||
diffWeeks != 0L -> if (diffWeeks == 1L) {
|
||||
res.getString(R.string.remaining_1week)
|
||||
} else res.getString(R.string.remaining_weeks, diffWeeks)
|
||||
} else {
|
||||
res.getString(R.string.remaining_weeks, diffWeeks)
|
||||
}
|
||||
diffDays != 0L -> if (diffDays == 1L) {
|
||||
res.getString(R.string.remaining_1day)
|
||||
} else res.getString(R.string.remaining_days, diffDays)
|
||||
} else {
|
||||
res.getString(R.string.remaining_days, diffDays)
|
||||
}
|
||||
diffHours != 0L -> if (diffHours == 1L) {
|
||||
res.getString(R.string.remaining_1hour)
|
||||
} else res.getString(R.string.remaining_hours, diffHours)
|
||||
} else {
|
||||
res.getString(R.string.remaining_hours, diffHours)
|
||||
}
|
||||
diffMinutes == 1L -> res.getString(R.string.remaining_1Minute)
|
||||
else -> res.getString(R.string.remaining_minutes, diffMinutes)
|
||||
}
|
||||
|
|
@ -14,7 +14,8 @@ import java.util.Locale
|
|||
fun String.parseToZonedDateTime(): ZonedDateTime? {
|
||||
val parsed: TemporalAccessor = formatter().parseBest(
|
||||
this,
|
||||
ZonedDateTime::from, LocalDateTime::from
|
||||
ZonedDateTime::from,
|
||||
LocalDateTime::from
|
||||
)
|
||||
return if (parsed is ZonedDateTime) {
|
||||
parsed
|
||||
|
|
@ -40,4 +41,4 @@ fun formatter(): DateTimeFormatter =
|
|||
.appendPattern("['T'][' ']")
|
||||
.append(DateTimeFormatter.ISO_LOCAL_TIME)
|
||||
.appendPattern("[XX]")
|
||||
.toFormatter()
|
||||
.toFormatter()
|
||||
|
|
@ -171,7 +171,9 @@ class AdHandler(val activity: Activity, val type: AdType, val rewardAction: (Boo
|
|||
}
|
||||
|
||||
RewardedAd.load(
|
||||
activity, type.adUnitID, adRequest,
|
||||
activity,
|
||||
type.adUnitID,
|
||||
adRequest,
|
||||
object : RewardedAdLoadCallback() {
|
||||
override fun onAdFailedToLoad(adError: LoadAdError) {
|
||||
FirebaseCrashlytics.getInstance().recordException(Throwable(adError.message))
|
||||
|
|
|
|||
|
|
@ -0,0 +1,108 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import androidx.core.os.bundleOf
|
||||
import com.amplitude.android.Amplitude
|
||||
import com.amplitude.android.Configuration
|
||||
import com.amplitude.android.events.Identify
|
||||
import com.google.firebase.analytics.FirebaseAnalytics
|
||||
import com.google.firebase.crashlytics.FirebaseCrashlytics
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.shared.habitica.BuildConfig
|
||||
|
||||
enum class AnalyticsTarget {
|
||||
AMPLITUDE,
|
||||
FIREBASE
|
||||
}
|
||||
|
||||
enum class EventCategory(val key: String) {
|
||||
BEHAVIOUR("behaviour"),
|
||||
NAVIGATION("navigation")
|
||||
}
|
||||
|
||||
enum class HitType(val key: String) {
|
||||
EVENT("event"),
|
||||
PAGEVIEW("pageview"),
|
||||
CREATE_WIDGET("create"),
|
||||
REMOVE_WIDGET("remove"),
|
||||
UPDATE_WIDGET("update")
|
||||
}
|
||||
|
||||
object Analytics {
|
||||
private lateinit var firebase: FirebaseAnalytics
|
||||
private lateinit var amplitude: Amplitude
|
||||
|
||||
@JvmOverloads
|
||||
fun sendEvent(
|
||||
eventAction: String?,
|
||||
category: EventCategory?,
|
||||
hitType: HitType?,
|
||||
additionalData: Map<String, Any>? = null,
|
||||
target: AnalyticsTarget? = null
|
||||
) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
return
|
||||
}
|
||||
val data = mutableMapOf<String, Any?>(
|
||||
"eventAction" to eventAction,
|
||||
"eventCategory" to category?.key,
|
||||
"hitType" to hitType?.key,
|
||||
"status" to "displayed"
|
||||
)
|
||||
if (additionalData != null) {
|
||||
data.putAll(additionalData)
|
||||
}
|
||||
if (eventAction != null) {
|
||||
if (target == null || target == AnalyticsTarget.AMPLITUDE) {
|
||||
amplitude.track(eventAction, data)
|
||||
}
|
||||
if (target == null || target == AnalyticsTarget.FIREBASE) {
|
||||
firebase.logEvent(eventAction, bundleOf(*data.toList().toTypedArray()))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun sendNavigationEvent(page: String) {
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["page"] = page
|
||||
sendEvent("navigated", EventCategory.NAVIGATION, HitType.PAGEVIEW, additionalData)
|
||||
}
|
||||
|
||||
fun initialize(context: Context) {
|
||||
amplitude = Amplitude(
|
||||
Configuration(
|
||||
context.getString(R.string.amplitude_app_id),
|
||||
context
|
||||
)
|
||||
)
|
||||
firebase = FirebaseAnalytics.getInstance(context)
|
||||
}
|
||||
|
||||
fun identify(sharedPrefs: SharedPreferences) {
|
||||
val identify = Identify()
|
||||
.setOnce("androidStore", BuildConfig.STORE)
|
||||
sharedPrefs.getString("launch_screen", "")?.let {
|
||||
identify.set("launch_screen", it)
|
||||
}
|
||||
amplitude.identify(identify)
|
||||
}
|
||||
|
||||
fun setUserID(userID: String) {
|
||||
amplitude.setUserId(userID)
|
||||
FirebaseCrashlytics.getInstance().setUserId(userID)
|
||||
firebase.setUserId(userID)
|
||||
}
|
||||
|
||||
fun setUserProperty(identifier: String, value: String) {
|
||||
firebase.setUserProperty(identifier, value)
|
||||
}
|
||||
|
||||
fun logError(msg: String) {
|
||||
FirebaseCrashlytics.getInstance().log(msg)
|
||||
}
|
||||
|
||||
fun logException(t: Throwable) {
|
||||
FirebaseCrashlytics.getInstance().recordException(t)
|
||||
}
|
||||
}
|
||||
|
|
@ -27,7 +27,6 @@ class NotificationOpenHandler {
|
|||
PushNotificationManager.G1G1_PROMO_KEY -> openGiftOneGetOneInfoScreen()
|
||||
else -> {
|
||||
intent.getStringExtra("openURL")?.let {
|
||||
|
||||
MainNavigationController.navigate(it)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,26 +54,26 @@ import kotlin.time.DurationUnit
|
|||
import kotlin.time.toDuration
|
||||
|
||||
class PurchaseHandler(
|
||||
private val context : Context,
|
||||
private val analyticsManager : AnalyticsManager,
|
||||
private val apiClient : ApiClient,
|
||||
private val userViewModel : MainUserViewModel
|
||||
private val context: Context,
|
||||
private val analyticsManager: AnalyticsManager,
|
||||
private val apiClient: ApiClient,
|
||||
private val userViewModel: MainUserViewModel
|
||||
) : PurchasesUpdatedListener, PurchasesResponseListener {
|
||||
private val billingClient =
|
||||
BillingClient.newBuilder(context).setListener(this).enablePendingPurchases().build()
|
||||
|
||||
override fun onPurchasesUpdated(result : BillingResult, purchases : MutableList<Purchase>?) {
|
||||
override fun onPurchasesUpdated(result: BillingResult, purchases: MutableList<Purchase>?) {
|
||||
purchases?.let { processPurchases(result, it) }
|
||||
}
|
||||
|
||||
override fun onQueryPurchasesResponse(
|
||||
result : BillingResult,
|
||||
purchases : MutableList<Purchase>
|
||||
result: BillingResult,
|
||||
purchases: MutableList<Purchase>
|
||||
) {
|
||||
processPurchases(result, purchases)
|
||||
}
|
||||
|
||||
private fun processPurchases(result : BillingResult, purchases : List<Purchase>) {
|
||||
private fun processPurchases(result: BillingResult, purchases: List<Purchase>) {
|
||||
when (result.responseCode) {
|
||||
BillingClient.BillingResponseCode.OK -> {
|
||||
val mostRecentSub = findMostRecentSubscription(purchases)
|
||||
|
|
@ -86,9 +86,10 @@ class PurchaseHandler(
|
|||
purchase.products.firstOrNull()
|
||||
)
|
||||
) {
|
||||
if (((plan.dateTerminated != null) == purchase.isAutoRenewing)
|
||||
|| mostRecentSub?.orderId != purchase.orderId
|
||||
|| purchase.purchaseToken == plan.customerId) {
|
||||
if (((plan.dateTerminated != null) == purchase.isAutoRenewing) ||
|
||||
mostRecentSub?.orderId != purchase.orderId ||
|
||||
purchase.purchaseToken == plan.customerId
|
||||
) {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
|
@ -124,12 +125,12 @@ class PurchaseHandler(
|
|||
startListening()
|
||||
}
|
||||
|
||||
private var billingClientState : BillingClientState = BillingClientState.UNINITIALIZED
|
||||
private var billingClientState: BillingClientState = BillingClientState.UNINITIALIZED
|
||||
|
||||
private enum class BillingClientState {
|
||||
UNINITIALIZED, READY, UNAVAILABLE, DISCONNECTED, CONNECTING;
|
||||
|
||||
val canMaybePurchase : Boolean
|
||||
val canMaybePurchase: Boolean
|
||||
get() {
|
||||
return this == UNINITIALIZED || this == READY || this == CONNECTING
|
||||
}
|
||||
|
|
@ -150,7 +151,7 @@ class PurchaseHandler(
|
|||
}
|
||||
billingClientState = BillingClientState.CONNECTING
|
||||
billingClient.startConnection(object : BillingClientStateListener {
|
||||
override fun onBillingSetupFinished(billingResult : BillingResult) {
|
||||
override fun onBillingSetupFinished(billingResult: BillingResult) {
|
||||
if (billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
billingClientState = BillingClientState.READY
|
||||
MainScope().launchCatching {
|
||||
|
|
@ -216,18 +217,18 @@ class PurchaseHandler(
|
|||
suspend fun getAllGiftSubscriptionProducts() =
|
||||
getSKUs(BillingClient.ProductType.INAPP, PurchaseTypes.allSubscriptionNoRenewTypes)
|
||||
|
||||
suspend fun getInAppPurchaseSKU(identifier : String) =
|
||||
suspend fun getInAppPurchaseSKU(identifier: String) =
|
||||
getSKU(BillingClient.ProductType.INAPP, identifier)
|
||||
|
||||
private suspend fun getSKUs(type : String, identifiers : List<String>) =
|
||||
private suspend fun getSKUs(type: String, identifiers: List<String>) =
|
||||
loadInventory(type, identifiers) ?: emptyList()
|
||||
|
||||
private suspend fun getSKU(type : String, identifier : String) : ProductDetails? {
|
||||
private suspend fun getSKU(type: String, identifier: String): ProductDetails? {
|
||||
val inventory = loadInventory(type, listOf(identifier))
|
||||
return inventory?.firstOrNull()
|
||||
}
|
||||
|
||||
private suspend fun loadInventory(type : String, skus : List<String>) : List<ProductDetails>? {
|
||||
private suspend fun loadInventory(type: String, skus: List<String>): List<ProductDetails>? {
|
||||
retryUntil {
|
||||
if (billingClientState == BillingClientState.DISCONNECTED) {
|
||||
startListening()
|
||||
|
|
@ -246,11 +247,11 @@ class PurchaseHandler(
|
|||
}
|
||||
|
||||
fun purchase(
|
||||
activity : Activity,
|
||||
skuDetails : ProductDetails,
|
||||
recipient : String? = null,
|
||||
recipientUsername : String? = null,
|
||||
isSaleGemPurchase : Boolean = false
|
||||
activity: Activity,
|
||||
skuDetails: ProductDetails,
|
||||
recipient: String? = null,
|
||||
recipientUsername: String? = null,
|
||||
isSaleGemPurchase: Boolean = false
|
||||
) {
|
||||
this.isSaleGemPurchase = isSaleGemPurchase
|
||||
recipient?.let {
|
||||
|
|
@ -268,20 +269,23 @@ class PurchaseHandler(
|
|||
billingClient.launchBillingFlow(activity, flowParams)
|
||||
}
|
||||
|
||||
private suspend fun consume(purchase : Purchase, retries : Int = 4) {
|
||||
private suspend fun consume(purchase: Purchase, retries: Int = 4) {
|
||||
retryUntil { billingClientState.canMaybePurchase && billingClient.isReady }
|
||||
val params = ConsumeParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build()
|
||||
val result = billingClient.consumePurchase(params)
|
||||
if (result.billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
|
||||
if (result.billingResult.responseCode != BillingClient.BillingResponseCode.OK && retries > 0) {
|
||||
delay(500)
|
||||
consume(purchase, retries - 1)
|
||||
} else if (result.billingResult.responseCode != BillingClient.BillingResponseCode.OK) {
|
||||
// Throw an error to continue the flow
|
||||
throw Exception("Failed to consume purchase after multiple attempts")
|
||||
} else {
|
||||
userViewModel.userRepository.retrieveUser(false, true)
|
||||
}
|
||||
}
|
||||
|
||||
private var processedPurchases = mutableSetOf<String>()
|
||||
private fun handle(purchase : Purchase) {
|
||||
private fun handle(purchase: Purchase) {
|
||||
if (purchase.purchaseState != Purchase.PurchaseState.PURCHASED || processedPurchases.contains(purchase.orderId)) {
|
||||
return
|
||||
}
|
||||
|
|
@ -299,7 +303,7 @@ class PurchaseHandler(
|
|||
consume(purchase)
|
||||
}
|
||||
displayGryphatriceConfirmationDialog(purchase, gift?.third)
|
||||
} catch (throwable : Throwable) {
|
||||
} catch (throwable: Throwable) {
|
||||
handleError(throwable, purchase)
|
||||
}
|
||||
}
|
||||
|
|
@ -316,7 +320,7 @@ class PurchaseHandler(
|
|||
consume(purchase)
|
||||
}
|
||||
displayConfirmationDialog(purchase, gift?.third)
|
||||
} catch (throwable : Throwable) {
|
||||
} catch (throwable: Throwable) {
|
||||
handleError(throwable, purchase)
|
||||
}
|
||||
}
|
||||
|
|
@ -333,7 +337,7 @@ class PurchaseHandler(
|
|||
consume(purchase)
|
||||
}
|
||||
displayConfirmationDialog(purchase, gift?.third)
|
||||
} catch (throwable : Throwable) {
|
||||
} catch (throwable: Throwable) {
|
||||
handleError(throwable, purchase)
|
||||
}
|
||||
}
|
||||
|
|
@ -350,7 +354,7 @@ class PurchaseHandler(
|
|||
acknowledgePurchase(purchase)
|
||||
}
|
||||
displayConfirmationDialog(purchase)
|
||||
} catch (throwable : Throwable) {
|
||||
} catch (throwable: Throwable) {
|
||||
handleError(throwable, purchase)
|
||||
}
|
||||
}
|
||||
|
|
@ -358,7 +362,7 @@ class PurchaseHandler(
|
|||
}
|
||||
}
|
||||
|
||||
private suspend fun acknowledgePurchase(purchase : Purchase, retries : Int = 4) {
|
||||
private suspend fun acknowledgePurchase(purchase: Purchase, retries: Int = 4) {
|
||||
val params =
|
||||
AcknowledgePurchaseParams.newBuilder().setPurchaseToken(purchase.purchaseToken).build()
|
||||
val response = billingClient.acknowledgePurchase(params)
|
||||
|
|
@ -368,13 +372,13 @@ class PurchaseHandler(
|
|||
}
|
||||
}
|
||||
|
||||
private fun processedPurchase(purchase : Purchase) {
|
||||
private fun processedPurchase(purchase: Purchase) {
|
||||
MainScope().launch(ExceptionHandler.coroutine()) {
|
||||
userViewModel.userRepository.retrieveUser(false, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildValidationRequest(purchase : Purchase) : PurchaseValidationRequest {
|
||||
private fun buildValidationRequest(purchase: Purchase): PurchaseValidationRequest {
|
||||
val validationRequest = PurchaseValidationRequest()
|
||||
validationRequest.sku = purchase.products.firstOrNull()
|
||||
validationRequest.transaction = Transaction()
|
||||
|
|
@ -392,17 +396,26 @@ class PurchaseHandler(
|
|||
return validationRequest
|
||||
}
|
||||
|
||||
private fun handleError(throwable : Throwable, purchase : Purchase) {
|
||||
(throwable as? HttpException)?.let { error ->
|
||||
if (error.code() == 401) {
|
||||
val res = apiClient.getErrorResponse(throwable)
|
||||
if (res.message != null && res.message == "RECEIPT_ALREADY_USED") {
|
||||
processedPurchase(purchase)
|
||||
removeGift(purchase.products.firstOrNull())
|
||||
CoroutineScope(Dispatchers.IO).launch(ExceptionHandler.coroutine()) {
|
||||
consume(purchase)
|
||||
private fun handleError(throwable: Throwable, purchase: Purchase) {
|
||||
when (throwable) {
|
||||
is HttpException -> {
|
||||
if (throwable.code() == 401) {
|
||||
val res = apiClient.getErrorResponse(throwable)
|
||||
if (res.message != null && res.message == "RECEIPT_ALREADY_USED") {
|
||||
processedPurchase(purchase)
|
||||
removeGift(purchase.products.firstOrNull())
|
||||
CoroutineScope(Dispatchers.IO).launch(ExceptionHandler.coroutine()) {
|
||||
consume(purchase)
|
||||
}
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// Handles other potential errors such as IOException or an exception
|
||||
// thrown by billingClient.consumePurchase method that is not handled
|
||||
CoroutineScope(Dispatchers.IO).launch(ExceptionHandler.coroutine()) {
|
||||
consume(purchase)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -410,24 +423,24 @@ class PurchaseHandler(
|
|||
FirebaseCrashlytics.getInstance().recordException(throwable)
|
||||
}
|
||||
|
||||
suspend fun checkForSubscription() : Purchase? {
|
||||
suspend fun checkForSubscription(): Purchase? {
|
||||
val result = withContext(Dispatchers.IO) {
|
||||
val params =
|
||||
QueryPurchasesParams.newBuilder().setProductType(BillingClient.ProductType.SUBS)
|
||||
.build()
|
||||
billingClient.queryPurchasesAsync(params)
|
||||
}
|
||||
val fallback : Purchase? = null
|
||||
val fallback: Purchase? = null
|
||||
if (result.billingResult.responseCode == BillingClient.BillingResponseCode.OK) {
|
||||
return findMostRecentSubscription(result.purchasesList)
|
||||
}
|
||||
return fallback
|
||||
}
|
||||
|
||||
private fun findMostRecentSubscription(purchasesList : List<Purchase>) : Purchase? {
|
||||
private fun findMostRecentSubscription(purchasesList: List<Purchase>): Purchase? {
|
||||
val purchases =
|
||||
purchasesList.filter { it.isAcknowledged }.sortedByDescending { it.purchaseTime }
|
||||
var fallback : Purchase? = null
|
||||
var fallback: Purchase? = null
|
||||
// If there is a subscription that is still active, prioritise that. Otherwise return the most recent one.
|
||||
for (purchase in purchases) {
|
||||
if (purchase.isAutoRenewing) {
|
||||
|
|
@ -440,14 +453,14 @@ class PurchaseHandler(
|
|||
}
|
||||
|
||||
private var alreadyTriedCancellation = false
|
||||
suspend fun cancelSubscription() : User? {
|
||||
suspend fun cancelSubscription(): User? {
|
||||
if (alreadyTriedCancellation) return null
|
||||
alreadyTriedCancellation = true
|
||||
apiClient.cancelSubscription()
|
||||
return userViewModel.userRepository.retrieveUser(false, true)
|
||||
}
|
||||
|
||||
private fun durationString(sku : String) : String {
|
||||
private fun durationString(sku: String): String {
|
||||
return when (sku) {
|
||||
PurchaseTypes.Subscription1MonthNoRenew, PurchaseTypes.Subscription1Month -> "1"
|
||||
PurchaseTypes.Subscription3MonthNoRenew, PurchaseTypes.Subscription3Month -> "3"
|
||||
|
|
@ -459,7 +472,7 @@ class PurchaseHandler(
|
|||
|
||||
private var isSaleGemPurchase = false
|
||||
|
||||
private fun gemAmountString(sku : String) : String {
|
||||
private fun gemAmountString(sku: String): String {
|
||||
if (isSaleGemPurchase) {
|
||||
isSaleGemPurchase = false
|
||||
return when (sku) {
|
||||
|
|
@ -482,7 +495,7 @@ class PurchaseHandler(
|
|||
|
||||
private val displayedConfirmations = mutableListOf<String>()
|
||||
|
||||
private fun displayConfirmationDialog(purchase : Purchase, giftedTo : String? = null) {
|
||||
private fun displayConfirmationDialog(purchase: Purchase, giftedTo: String? = null) {
|
||||
if (displayedConfirmations.contains(purchase.orderId)) {
|
||||
return
|
||||
}
|
||||
|
|
@ -496,7 +509,9 @@ class PurchaseHandler(
|
|||
PurchaseTypes.allSubscriptionNoRenewTypes.contains(sku) -> {
|
||||
title = context.getString(R.string.gift_confirmation_title)
|
||||
context.getString(
|
||||
R.string.gift_confirmation_text_sub, giftedTo, durationString(sku)
|
||||
R.string.gift_confirmation_text_sub,
|
||||
giftedTo,
|
||||
durationString(sku)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -505,7 +520,8 @@ class PurchaseHandler(
|
|||
context.getString(R.string.subscription_confirmation)
|
||||
} else {
|
||||
context.getString(
|
||||
R.string.subscription_confirmation_multiple, durationString(sku)
|
||||
R.string.subscription_confirmation_multiple,
|
||||
durationString(sku)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -513,7 +529,9 @@ class PurchaseHandler(
|
|||
PurchaseTypes.allGemTypes.contains(sku) && giftedTo != null -> {
|
||||
title = context.getString(R.string.gift_confirmation_title)
|
||||
context.getString(
|
||||
R.string.gift_confirmation_text_gems_new, giftedTo, gemAmountString(sku)
|
||||
R.string.gift_confirmation_text_gems_new,
|
||||
giftedTo,
|
||||
gemAmountString(sku)
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -539,8 +557,8 @@ class PurchaseHandler(
|
|||
}
|
||||
|
||||
private fun displayGryphatriceConfirmationDialog(
|
||||
purchase : Purchase,
|
||||
giftedTo : String? = null
|
||||
purchase: Purchase,
|
||||
giftedTo: String? = null
|
||||
) {
|
||||
MainScope().launch(ExceptionHandler.coroutine()) {
|
||||
val application = (context as? HabiticaBaseApplication)
|
||||
|
|
@ -568,15 +586,15 @@ class PurchaseHandler(
|
|||
|
||||
companion object {
|
||||
private const val PENDING_GIFTS_KEY = "PENDING_GIFTS_DATED"
|
||||
private var pendingGifts : MutableMap<String, Triple<Date, String, String>> = HashMap()
|
||||
private var preferences : SharedPreferences? = null
|
||||
private var pendingGifts: MutableMap<String, Triple<Date, String, String>> = HashMap()
|
||||
private var preferences: SharedPreferences? = null
|
||||
|
||||
fun addGift(sku : String, userID : String, username : String) {
|
||||
fun addGift(sku: String, userID: String, username: String) {
|
||||
pendingGifts[sku] = Triple(Date(), userID, username)
|
||||
savePendingGifts()
|
||||
}
|
||||
|
||||
private fun removeGift(sku : String?) : Triple<Date, String, String>? {
|
||||
private fun removeGift(sku: String?): Triple<Date, String, String>? {
|
||||
val gift = pendingGifts.remove(sku)
|
||||
savePendingGifts()
|
||||
return gift
|
||||
|
|
@ -593,11 +611,11 @@ class PurchaseHandler(
|
|||
}
|
||||
|
||||
suspend fun retryUntil(
|
||||
times : Int = Int.MAX_VALUE,
|
||||
initialDelay : Long = 100, // 0.1 second
|
||||
maxDelay : Long = 1000, // 1 second
|
||||
factor : Double = 2.0,
|
||||
block : suspend () -> Boolean
|
||||
times: Int = Int.MAX_VALUE,
|
||||
initialDelay: Long = 100, // 0.1 second
|
||||
maxDelay: Long = 1000, // 1 second
|
||||
factor: Double = 2.0,
|
||||
block: suspend () -> Boolean
|
||||
) {
|
||||
var currentDelay = initialDelay
|
||||
repeat(times - 1) {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ import java.util.Date
|
|||
class TaskAlarmManager(
|
||||
private var context: Context,
|
||||
private var taskRepository: TaskRepository,
|
||||
private var authenticationHandler : AuthenticationHandler
|
||||
private var authenticationHandler: AuthenticationHandler
|
||||
) {
|
||||
private val am: AlarmManager? = context.getSystemService(Context.ALARM_SERVICE) as? AlarmManager
|
||||
|
||||
|
|
|
|||
|
|
@ -130,7 +130,8 @@ class TaskDescriptionBuilder(private val context: Context) {
|
|||
Frequency.WEEKLY -> context.resources.getQuantityString(R.plurals.repeat_weekly, everyX, everyX)
|
||||
Frequency.MONTHLY -> context.resources.getQuantityString(
|
||||
R.plurals.repeat_monthly,
|
||||
everyX, everyX
|
||||
everyX,
|
||||
everyX
|
||||
)
|
||||
Frequency.YEARLY -> context.resources.getQuantityString(R.plurals.repeat_yearly, everyX, everyX)
|
||||
null -> ""
|
||||
|
|
|
|||
|
|
@ -34,7 +34,9 @@ class GroupActivityNotification(context: Context, identifier: String?) : Habitic
|
|||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
|
||||
val existingNotifications = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val oldMessages = existingNotifications?.firstOrNull()?.notification?.extras?.getBundle("messages")?.get("messages") as? ArrayList<Map<String, String>> ?: arrayListOf()
|
||||
for (oldMessage in oldMessages) {
|
||||
style = style.addMessage(makeMessageFromData(oldMessage))
|
||||
|
|
@ -75,7 +77,8 @@ class GroupActivityNotification(context: Context, identifier: String?) : Habitic
|
|||
intent.putExtra("NOTIFICATION_ID", notificationId)
|
||||
val replyPendingIntent: PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, groupID.hashCode(),
|
||||
context,
|
||||
groupID.hashCode(),
|
||||
intent,
|
||||
withMutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
|
|
@ -83,7 +86,8 @@ class GroupActivityNotification(context: Context, identifier: String?) : Habitic
|
|||
val action: NotificationCompat.Action =
|
||||
NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_send_grey_600_24dp,
|
||||
context.getString(R.string.reply), replyPendingIntent
|
||||
context.getString(R.string.reply),
|
||||
replyPendingIntent
|
||||
)
|
||||
.addRemoteInput(remoteInput)
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -19,7 +19,9 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
|
|||
val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as? NotificationManager
|
||||
val existingNotifications = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
|
||||
notificationManager?.activeNotifications?.filter { it.id == getNotificationID(data) }
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
val messageText = EmojiParser.parseEmojis(data["message"]?.trim { it <= ' ' })
|
||||
val oldMessages = existingNotifications?.firstOrNull()?.notification?.extras?.getStringArrayList("messages") ?: arrayListOf()
|
||||
var style = NotificationCompat.InboxStyle()
|
||||
|
|
@ -73,7 +75,8 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
|
|||
intent.putExtra("NOTIFICATION_ID", notificationId)
|
||||
val replyPendingIntent: PendingIntent =
|
||||
PendingIntent.getBroadcast(
|
||||
context, senderID.hashCode(),
|
||||
context,
|
||||
senderID.hashCode(),
|
||||
intent,
|
||||
withMutableFlag(PendingIntent.FLAG_UPDATE_CURRENT)
|
||||
)
|
||||
|
|
@ -81,7 +84,8 @@ class ReceivedPrivateMessageLocalNotification(context: Context, identifier: Stri
|
|||
val action: NotificationCompat.Action =
|
||||
NotificationCompat.Action.Builder(
|
||||
R.drawable.ic_send_grey_600_24dp,
|
||||
context.getString(R.string.reply), replyPendingIntent
|
||||
context.getString(R.string.reply),
|
||||
replyPendingIntent
|
||||
)
|
||||
.addRemoteInput(remoteInput)
|
||||
.build()
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@ import javax.inject.Inject
|
|||
class BuyRewardUseCase @Inject
|
||||
constructor(
|
||||
private val taskRepository: TaskRepository,
|
||||
private val soundManager: SoundManager,
|
||||
private val soundManager: SoundManager
|
||||
) : UseCase<BuyRewardUseCase.RequestValues, TaskScoringResult?>() {
|
||||
|
||||
override suspend fun run(requestValues: RequestValues): TaskScoringResult? {
|
||||
|
|
|
|||
|
|
@ -21,8 +21,9 @@ constructor(private val soundManager: SoundManager) :
|
|||
val snackbarText = StringBuilder(data?.drop?.dialog ?: "")
|
||||
|
||||
if ((data?.questItemsFound ?: 0) > 0 && requestValues.showQuestItems) {
|
||||
if (snackbarText.isNotEmpty())
|
||||
if (snackbarText.isNotEmpty()) {
|
||||
snackbarText.append('\n')
|
||||
}
|
||||
snackbarText.append(
|
||||
requestValues.context.getString(
|
||||
R.string.quest_items_found,
|
||||
|
|
@ -36,7 +37,9 @@ constructor(private val soundManager: SoundManager) :
|
|||
delay(3000L)
|
||||
HabiticaSnackbar.showSnackbar(
|
||||
requestValues.snackbarTargetView,
|
||||
snackbarText, HabiticaSnackbar.SnackbarDisplayType.DROP, true
|
||||
snackbarText,
|
||||
HabiticaSnackbar.SnackbarDisplayType.DROP,
|
||||
true
|
||||
)
|
||||
soundManager.loadAndPlayAudio(SoundManager.SoundItemDrop)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,7 +19,7 @@ import javax.inject.Inject
|
|||
|
||||
class FeedPetUseCase @Inject
|
||||
constructor(
|
||||
private val inventoryRepository: InventoryRepository,
|
||||
private val inventoryRepository: InventoryRepository
|
||||
) : UseCase<FeedPetUseCase.RequestValues, FeedResponse?>() {
|
||||
override suspend fun run(requestValues: FeedPetUseCase.RequestValues): FeedResponse? {
|
||||
val feedResponse = inventoryRepository.feedPet(requestValues.pet, requestValues.food)
|
||||
|
|
|
|||
|
|
@ -22,4 +22,4 @@ class InsufficientGemsUseCase @Inject constructor(
|
|||
}
|
||||
|
||||
class RequestValues(val gemPrice: Int, val activity: Activity) : UseCase.RequestValues
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -52,7 +52,8 @@ constructor(
|
|||
HabiticaSnackbar.showSnackbar(
|
||||
requestValues.snackbarTargetView,
|
||||
requestValues.activity.getString(R.string.levelup_header, requestValues.newLevel),
|
||||
HabiticaSnackbar.SnackbarDisplayType.SUCCESS, true
|
||||
HabiticaSnackbar.SnackbarDisplayType.SUCCESS,
|
||||
true
|
||||
)
|
||||
return requestValues.user.stats
|
||||
}
|
||||
|
|
|
|||
|
|
@ -74,7 +74,6 @@ constructor(
|
|||
questDamage: Double?,
|
||||
user: User?
|
||||
): Pair<View, SnackbarDisplayType> {
|
||||
|
||||
var displayType = SnackbarDisplayType.SUCCESS
|
||||
|
||||
val container = LinearLayout(context)
|
||||
|
|
|
|||
|
|
@ -10,8 +10,10 @@ open class TeamPlan : RealmObject(), BaseObject {
|
|||
|
||||
var userID: String? = null
|
||||
var summary: String = ""
|
||||
|
||||
@SerializedName("leader")
|
||||
var leaderID: String? = null
|
||||
|
||||
// var managers: RealmList<String> = RealmList()
|
||||
var isActive: Boolean = false
|
||||
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ open class TutorialStep : RealmObject(), BaseMainObject {
|
|||
val flagPath: String
|
||||
get() = "flags.tutorial.$tutorialGroup.$identifier"
|
||||
|
||||
val linkFAQ : Boolean
|
||||
val linkFAQ: Boolean
|
||||
get() {
|
||||
return identifier == "party"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ open class WorldState : RealmObject(), BaseObject {
|
|||
var npcImageSuffix: String? = null
|
||||
|
||||
var currentEvent: WorldStateEvent? = null
|
||||
|
||||
@SerializedName("currentEventList")
|
||||
var events: RealmList<WorldStateEvent> = RealmList()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import io.realm.annotations.RealmClass
|
|||
open class LocalAuthentication : RealmObject(), BaseObject, AvatarLocalAuthentication {
|
||||
override var username: String? = null
|
||||
var email: String? = null
|
||||
|
||||
@SerializedName("has_password")
|
||||
var hasPassword: Boolean? = false
|
||||
}
|
||||
|
|
|
|||
|
|
@ -61,10 +61,12 @@ open class Customization : RealmObject(), BaseObject {
|
|||
"hair" -> {
|
||||
return if (identifier == "0") {
|
||||
"head_0"
|
||||
} else when (this.category) {
|
||||
"color" -> "hair_bangs_1_$identifier"
|
||||
"flower" -> "hair_flower_$identifier"
|
||||
else -> "hair_" + this.category + "_" + identifier + "_" + hairColor
|
||||
} else {
|
||||
when (this.category) {
|
||||
"color" -> "hair_bangs_1_$identifier"
|
||||
"flower" -> "hair_flower_$identifier"
|
||||
else -> "hair_" + this.category + "_" + identifier + "_" + hairColor
|
||||
}
|
||||
}
|
||||
}
|
||||
"background" -> return "background_$identifier"
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ open class Equipment : RealmObject(), BaseMainObject {
|
|||
|
||||
var value: Double = 0.toDouble()
|
||||
var type: String? = ""
|
||||
|
||||
@PrimaryKey
|
||||
var key: String? = ""
|
||||
var klass: String = ""
|
||||
|
|
@ -20,6 +21,7 @@ open class Equipment : RealmObject(), BaseMainObject {
|
|||
var con: Int = 0
|
||||
var str: Int = 0
|
||||
var per: Int = 0
|
||||
|
||||
@SerializedName("int")
|
||||
var _int: Int = 0
|
||||
var owned: Boolean? = null
|
||||
|
|
|
|||
|
|
@ -11,13 +11,17 @@ open class Mount : RealmObject(), Animal {
|
|||
get() {
|
||||
return if (field.isBlank()) {
|
||||
key?.split("-")?.toTypedArray()?.get(0) ?: ""
|
||||
} else field
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}
|
||||
override var color: String = ""
|
||||
get() {
|
||||
return if (field.isBlank()) {
|
||||
key?.split("-")?.toTypedArray()?.get(1) ?: ""
|
||||
} else field
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}
|
||||
override var text: String? = null
|
||||
override var type: String? = null
|
||||
|
|
@ -25,6 +29,7 @@ open class Mount : RealmObject(), Animal {
|
|||
|
||||
@Ignore
|
||||
override var numberOwned: Int = 0
|
||||
|
||||
@Ignore
|
||||
override var totalNumber: Int = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -11,13 +11,17 @@ open class Pet : RealmObject(), Animal {
|
|||
get() {
|
||||
return if (field.isBlank()) {
|
||||
key?.split("-")?.toTypedArray()?.get(0) ?: ""
|
||||
} else field
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}
|
||||
override var color: String = ""
|
||||
get() {
|
||||
return if (field.isBlank()) {
|
||||
key?.split("-")?.toTypedArray()?.get(1) ?: ""
|
||||
} else field
|
||||
} else {
|
||||
field
|
||||
}
|
||||
}
|
||||
override var text: String? = null
|
||||
override var type: String? = null
|
||||
|
|
@ -25,6 +29,7 @@ open class Pet : RealmObject(), Animal {
|
|||
|
||||
@Ignore
|
||||
override var numberOwned: Int = 0
|
||||
|
||||
@Ignore
|
||||
override var totalNumber: Int = 0
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,3 @@
|
|||
package com.habitrpg.android.habitica.models.invitations
|
||||
|
||||
class InviteResponse {
|
||||
|
||||
}
|
||||
class InviteResponse
|
||||
|
|
|
|||
|
|
@ -26,7 +26,9 @@ open class MemberPreferences :
|
|||
} else {
|
||||
"chair_$field"
|
||||
}
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
var language: String? = null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,16 +8,20 @@ import com.habitrpg.shared.habitica.models.responses.TaskDirectionData
|
|||
class BulkTaskScoringData {
|
||||
@SerializedName("con")
|
||||
var constitution: Int? = null
|
||||
|
||||
@SerializedName("str")
|
||||
var strength: Int? = null
|
||||
|
||||
@SerializedName("per")
|
||||
var per: Int? = null
|
||||
|
||||
@SerializedName("int")
|
||||
var intelligence: Int? = null
|
||||
var training: Training? = null
|
||||
var buffs: Buffs? = null
|
||||
var points: Int? = null
|
||||
var lvl: Int? = null
|
||||
|
||||
@SerializedName("class")
|
||||
var habitClass: String? = null
|
||||
var gp: Double? = null
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ open class ShopItem : RealmObject(), BaseObject {
|
|||
var key: String = ""
|
||||
var text: String? = ""
|
||||
var notes: String? = ""
|
||||
|
||||
@SerializedName("class")
|
||||
var imageName: String? = null
|
||||
get() {
|
||||
|
|
@ -44,9 +45,11 @@ open class ShopItem : RealmObject(), BaseObject {
|
|||
var unlockPath: String? = null
|
||||
var isSuggested: String? = null
|
||||
var pinType: String? = null
|
||||
|
||||
@SerializedName("klass")
|
||||
var habitClass: String? = null
|
||||
var previous: String? = null
|
||||
|
||||
@SerializedName("lvl")
|
||||
var level: Int? = null
|
||||
var event: ItemEvent? = null
|
||||
|
|
|
|||
|
|
@ -33,6 +33,7 @@ open class Challenge : RealmObject(), BaseMainObject {
|
|||
var group: Group? = null
|
||||
|
||||
var leader: User? = null
|
||||
|
||||
@Ignore
|
||||
var tasksOrder: TasksOrder? = null
|
||||
var summary: String? = null
|
||||
|
|
@ -69,7 +70,9 @@ open class Challenge : RealmObject(), BaseMainObject {
|
|||
override fun equals(other: Any?): Boolean {
|
||||
return if (other?.javaClass == Challenge::class.java && this.id != null) {
|
||||
this.id == (other as Challenge).id
|
||||
} else super.equals(other)
|
||||
} else {
|
||||
super.equals(other)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -13,6 +13,7 @@ open class UserParty : RealmObject(), BaseObject {
|
|||
var id: String = ""
|
||||
var quest: Quest? = null
|
||||
var seeking: Date? = null
|
||||
|
||||
@SerializedName("order")
|
||||
var partyOrder: String? = null // Order to display ppl
|
||||
var orderAscending: String? = null // Order type
|
||||
|
|
|
|||
|
|
@ -66,7 +66,9 @@ open class ChecklistItem : RealmObject, BaseMainObject, Parcelable {
|
|||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is ChecklistItem) {
|
||||
this.id == other.id
|
||||
} else super.equals(other)
|
||||
} else {
|
||||
super.equals(other)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
|
|||
|
|
@ -51,7 +51,9 @@ open class RemindersItem : RealmObject, Parcelable {
|
|||
override fun equals(other: Any?): Boolean {
|
||||
return if (other is RemindersItem) {
|
||||
this.id == other.id
|
||||
} else super.equals(other)
|
||||
} else {
|
||||
super.equals(other)
|
||||
}
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
|
|
@ -71,7 +73,8 @@ open class RemindersItem : RealmObject, Parcelable {
|
|||
|
||||
val parsed: TemporalAccessor = formatter.parseBest(
|
||||
time,
|
||||
ZonedDateTime::from, LocalDateTime::from
|
||||
ZonedDateTime::from,
|
||||
LocalDateTime::from
|
||||
)
|
||||
return if (parsed is ZonedDateTime) {
|
||||
parsed
|
||||
|
|
@ -91,7 +94,8 @@ open class RemindersItem : RealmObject, Parcelable {
|
|||
|
||||
val parsed: TemporalAccessor = formatter.parseBest(
|
||||
time,
|
||||
ZonedDateTime::from, LocalDateTime::from
|
||||
ZonedDateTime::from,
|
||||
LocalDateTime::from
|
||||
)
|
||||
return if (parsed is ZonedDateTime) {
|
||||
parsed.withZoneSameLocal(ZoneId.systemDefault())?.toInstant()
|
||||
|
|
|
|||
|
|
@ -69,15 +69,18 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
var dateCreated: Date? = null
|
||||
var position: Int = 0
|
||||
var group: TaskGroupPlan? = null
|
||||
|
||||
// Habits
|
||||
var up: Boolean? = false
|
||||
var down: Boolean? = false
|
||||
override var counterUp: Int? = 0
|
||||
override var counterDown: Int? = 0
|
||||
|
||||
// todos/dailies
|
||||
override var completed: Boolean = false
|
||||
var checklist: RealmList<ChecklistItem>? = RealmList()
|
||||
var reminders: RealmList<RemindersItem>? = RealmList()
|
||||
|
||||
// dailies
|
||||
var frequency: Frequency?
|
||||
get() = Frequency.from(frequencyValue)
|
||||
|
|
@ -87,11 +90,14 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
override var streak: Int? = 0
|
||||
var startDate: Date? = null
|
||||
var repeat: Days? = null
|
||||
|
||||
// todos
|
||||
@SerializedName("date")
|
||||
var dueDate: Date? = null
|
||||
|
||||
@Ignore
|
||||
var parsedText: Spanned? = null
|
||||
|
||||
@Ignore
|
||||
var parsedNotes: Spanned? = null
|
||||
|
||||
|
|
@ -370,8 +376,6 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
fun parseMarkdown() {
|
||||
parsedText = MarkdownParser.parseMarkdown(text)
|
||||
parsedNotes = MarkdownParser.parseMarkdown(notes)
|
||||
|
|
@ -421,7 +425,6 @@ open class Task : RealmObject, BaseMainObject, Parcelable, BaseTask {
|
|||
}
|
||||
|
||||
fun isBeingEdited(task: Task): Boolean {
|
||||
|
||||
when {
|
||||
text != task.text -> return true
|
||||
notes != task.notes -> return true
|
||||
|
|
|
|||
|
|
@ -22,12 +22,16 @@ open class Authentication : RealmObject(), BaseObject, AvatarAuthentication {
|
|||
var blocked: Boolean = false
|
||||
val hasPassword: Boolean
|
||||
get() = localAuthentication?.hasPassword == true
|
||||
|
||||
@SerializedName("local")
|
||||
override var localAuthentication: LocalAuthentication? = null
|
||||
|
||||
@SerializedName("google")
|
||||
var googleAuthentication: SocialAuthentication? = null
|
||||
|
||||
@SerializedName("apple")
|
||||
var appleAuthentication: SocialAuthentication? = null
|
||||
|
||||
@SerializedName("facebook")
|
||||
var facebookAuthentication: SocialAuthentication? = null
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@ import java.util.Date
|
|||
open class AuthenticationTimestamps : RealmObject(), BaseObject {
|
||||
@SerializedName("loggedin")
|
||||
var lastLoggedIn: Date? = null
|
||||
|
||||
@SerializedName("created")
|
||||
var createdAt: Date? = null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@ open class Outfit : RealmObject(), BaseObject, AvatarOutfit {
|
|||
override var head: String = ""
|
||||
override var shield: String = ""
|
||||
override var weapon: String = ""
|
||||
|
||||
@SerializedName("eyewear")
|
||||
override var eyeWear: String = ""
|
||||
override var headAccessory: String = ""
|
||||
|
|
|
|||
|
|
@ -11,8 +11,10 @@ open class Preferences : RealmObject(), AvatarPreferences, BaseObject {
|
|||
override var hair: Hair? = null
|
||||
var suppressModals: SuppressedModals? = null
|
||||
override var costume: Boolean = false
|
||||
|
||||
@SerializedName("disableClasses")
|
||||
override var disableClasses: Boolean = false
|
||||
|
||||
@SerializedName("sleep")
|
||||
override var sleep: Boolean = false
|
||||
var dailyDueDefaultView: Boolean = false
|
||||
|
|
@ -30,7 +32,9 @@ open class Preferences : RealmObject(), AvatarPreferences, BaseObject {
|
|||
} else {
|
||||
"chair_" + field!!
|
||||
}
|
||||
} else "chair_none"
|
||||
} else {
|
||||
"chair_none"
|
||||
}
|
||||
}
|
||||
var language: String? = null
|
||||
var sound: String? = null
|
||||
|
|
|
|||
|
|
@ -10,16 +10,20 @@ import io.realm.annotations.RealmClass
|
|||
open class Stats : RealmObject(), AvatarStats, BaseObject {
|
||||
@SerializedName("con")
|
||||
var constitution: Int? = null
|
||||
|
||||
@SerializedName("str")
|
||||
var strength: Int? = null
|
||||
|
||||
@SerializedName("per")
|
||||
var per: Int? = null
|
||||
|
||||
@SerializedName("int")
|
||||
var intelligence: Int? = null
|
||||
var training: Training? = null
|
||||
override var buffs: Buffs? = null
|
||||
override var points: Int? = null
|
||||
override var lvl: Int? = null
|
||||
|
||||
@SerializedName("class")
|
||||
override var habitClass: String? = null
|
||||
override var gp: Double? = null
|
||||
|
|
|
|||
|
|
@ -43,7 +43,9 @@ open class SubscriptionPlan : RealmObject(), BaseObject {
|
|||
get() {
|
||||
return if (isActive) {
|
||||
25 + (consecutive?.gemCapExtra ?: 0)
|
||||
} else 0
|
||||
} else {
|
||||
0
|
||||
}
|
||||
}
|
||||
|
||||
val numberOfGemsLeft: Int
|
||||
|
|
|
|||
|
|
@ -58,6 +58,7 @@ open class User : RealmObject(), BaseMainObject, Avatar, VersionedObject {
|
|||
var profile: Profile? = null
|
||||
var party: UserParty? = null
|
||||
override var items: Items? = null
|
||||
|
||||
@SerializedName("auth")
|
||||
override var authentication: Authentication? = null
|
||||
override var flags: Flags? = null
|
||||
|
|
|
|||
|
|
@ -60,10 +60,11 @@ class AppModule {
|
|||
): KeyHelper? {
|
||||
return if (keyStore == null) {
|
||||
null
|
||||
} else getInstance(context, sharedPreferences, keyStore)
|
||||
} else {
|
||||
getInstance(context, sharedPreferences, keyStore)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesAuthenticationHandler(sharedPreferences: SharedPreferences): AuthenticationHandler {
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ open class RepositoryModule {
|
|||
contentLocalRepository: ContentLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
@ApplicationContext context: Context,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): ContentRepository {
|
||||
return ContentRepositoryImpl(
|
||||
contentLocalRepository,
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class AuthenticationHandler {
|
|||
val isAuthenticated: Boolean
|
||||
get() = currentUserID != null
|
||||
|
||||
constructor(sharedPreferences : SharedPreferences) {
|
||||
constructor(sharedPreferences: SharedPreferences) {
|
||||
_userIDFlow.value = sharedPreferences.getString("UserID", "") ?: ""
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -69,7 +69,7 @@ class UserRepositoryModule {
|
|||
fun providesTaskRepository(
|
||||
localRepository: TaskLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler,
|
||||
authenticationHandler: AuthenticationHandler,
|
||||
appConfigManager: AppConfigManager,
|
||||
analyticsManager: AnalyticsManager
|
||||
): TaskRepository {
|
||||
|
|
@ -91,7 +91,7 @@ class UserRepositoryModule {
|
|||
fun providesTagRepository(
|
||||
localRepository: TagLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): TagRepository {
|
||||
return TagRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
|
@ -105,7 +105,7 @@ class UserRepositoryModule {
|
|||
fun providesChallengeRepository(
|
||||
localRepository: ChallengeLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): ChallengeRepository {
|
||||
return ChallengeRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ class UserRepositoryModule {
|
|||
fun providesUserRepository(
|
||||
localRepository: UserLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler,
|
||||
authenticationHandler: AuthenticationHandler,
|
||||
taskRepository: TaskRepository,
|
||||
appConfigManager: AppConfigManager,
|
||||
analyticsManager: AnalyticsManager
|
||||
|
|
@ -143,14 +143,15 @@ class UserRepositoryModule {
|
|||
fun providesSocialRepository(
|
||||
localRepository: SocialLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): SocialRepository {
|
||||
return SocialRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
||||
@Provides
|
||||
fun providesInventoryLocalRepository(
|
||||
realm: Realm): InventoryLocalRepository {
|
||||
realm: Realm
|
||||
): InventoryLocalRepository {
|
||||
return RealmInventoryLocalRepository(realm)
|
||||
}
|
||||
|
||||
|
|
@ -158,7 +159,7 @@ class UserRepositoryModule {
|
|||
fun providesInventoryRepository(
|
||||
localRepository: InventoryLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler,
|
||||
authenticationHandler: AuthenticationHandler,
|
||||
remoteConfig: AppConfigManager
|
||||
): InventoryRepository {
|
||||
return InventoryRepositoryImpl(localRepository, apiClient, authenticationHandler, remoteConfig)
|
||||
|
|
@ -173,7 +174,7 @@ class UserRepositoryModule {
|
|||
fun providesFAQRepository(
|
||||
localRepository: FAQLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): FAQRepository {
|
||||
return FAQRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
|
@ -187,7 +188,7 @@ class UserRepositoryModule {
|
|||
fun providesTutorialRepository(
|
||||
localRepository: TutorialLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): TutorialRepository {
|
||||
return TutorialRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
|
@ -201,7 +202,7 @@ class UserRepositoryModule {
|
|||
fun providesCustomizationRepository(
|
||||
localRepository: CustomizationLocalRepository,
|
||||
apiClient: ApiClient,
|
||||
authenticationHandler : AuthenticationHandler
|
||||
authenticationHandler: AuthenticationHandler
|
||||
): CustomizationRepository {
|
||||
return CustomizationRepositoryImpl(localRepository, apiClient, authenticationHandler)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,8 +32,10 @@ class NotificationPublisher : BroadcastReceiver() {
|
|||
|
||||
@Inject
|
||||
lateinit var taskRepository: TaskRepository
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
|
|
@ -44,7 +46,7 @@ class NotificationPublisher : BroadcastReceiver() {
|
|||
this.context = context
|
||||
if (!wasInjected) {
|
||||
wasInjected = true
|
||||
}
|
||||
}
|
||||
|
||||
var wasInactive = false
|
||||
// Show special notification if user hasn't logged in for a week
|
||||
|
|
@ -121,8 +123,10 @@ class NotificationPublisher : BroadcastReceiver() {
|
|||
notificationIntent.flags = Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_SINGLE_TOP
|
||||
|
||||
val intent = PendingIntent.getActivity(
|
||||
thisContext, 0,
|
||||
notificationIntent, withImmutableFlag(0)
|
||||
thisContext,
|
||||
0,
|
||||
notificationIntent,
|
||||
withImmutableFlag(0)
|
||||
)
|
||||
builder.setContentIntent(intent)
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ class TaskAlarmBootReceiver : BroadcastReceiver() {
|
|||
|
||||
@Inject
|
||||
lateinit var taskAlarmManager: TaskAlarmManager
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPreferences: SharedPreferences
|
||||
|
||||
|
|
|
|||
|
|
@ -17,7 +17,8 @@ class GemPurchaseOptionsView(context: Context, attrs: AttributeSet) : FrameLayou
|
|||
val a = context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.GemPurchaseOptionsView,
|
||||
0, 0
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
binding.gemAmount.text = a.getText(R.styleable.GemPurchaseOptionsView_gemAmount)
|
||||
|
|
|
|||
|
|
@ -16,7 +16,8 @@ class SpeechBubbleView(context: Context, attrs: AttributeSet) : FrameLayout(cont
|
|||
val attributes = context.theme.obtainStyledAttributes(
|
||||
attrs,
|
||||
R.styleable.SpeechBubbleView,
|
||||
0, 0
|
||||
0,
|
||||
0
|
||||
)
|
||||
|
||||
binding.namePlate.text = attributes.getString(R.styleable.SpeechBubbleView_namePlate)
|
||||
|
|
|
|||
|
|
@ -75,7 +75,9 @@ class AdventureGuideActivity : BaseActivity() {
|
|||
return if (item.itemId == android.R.id.home) {
|
||||
NavUtils.navigateUpFromSameTask(this)
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
} else {
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateUser(user: User) {
|
||||
|
|
|
|||
|
|
@ -42,8 +42,10 @@ class ArmoireActivity : BaseActivity() {
|
|||
|
||||
@Inject
|
||||
internal lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
internal lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
|
|
@ -156,7 +158,8 @@ class ArmoireActivity : BaseActivity() {
|
|||
createParticles(container, R.drawable.confetti_red)
|
||||
createParticles(container, R.drawable.confetti_yellow)
|
||||
createParticles(container, R.drawable.confetti_purple)
|
||||
}, 500
|
||||
},
|
||||
500
|
||||
)
|
||||
|
||||
binding.iconView.startAnimation(Animations.bobbingAnimation())
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ import javax.inject.Inject
|
|||
abstract class BaseActivity : AppCompatActivity() {
|
||||
@Inject
|
||||
lateinit var notificationsManager: NotificationsManager
|
||||
|
||||
@Inject
|
||||
lateinit var userRepository: UserRepository
|
||||
|
||||
@Inject
|
||||
internal lateinit var analyticsManager: AnalyticsManager
|
||||
|
||||
|
|
|
|||
|
|
@ -559,7 +559,8 @@ fun PotionGrid() {
|
|||
model = DataBindingUtils.BASE_IMAGE_URL + DataBindingUtils.getFullFilename(
|
||||
"Pet_HatchingPotion_$potion"
|
||||
),
|
||||
null, Modifier.size(68.dp)
|
||||
null,
|
||||
Modifier.size(68.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,12 +15,9 @@ import android.widget.ArrayAdapter
|
|||
import android.widget.EditText
|
||||
import android.widget.TextView
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.widget.AppCompatCheckedTextView
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.ChallengeRepository
|
||||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
|
|
@ -230,7 +227,10 @@ class ChallengeFormActivity : BaseActivity() {
|
|||
tasksViewModel = ViewModelProvider(this)[TasksViewModel::class.java]
|
||||
|
||||
ChallengeTasksRecyclerViewAdapter(
|
||||
tasksViewModel, 0, this, "",
|
||||
tasksViewModel,
|
||||
0,
|
||||
this,
|
||||
"",
|
||||
openTaskDisabled = false,
|
||||
taskActionsDisabled = true
|
||||
).also { challengeTasks = it }
|
||||
|
|
@ -499,7 +499,9 @@ class ChallengeFormActivity : BaseActivity() {
|
|||
taskList.remove(addReward)
|
||||
|
||||
return challengeRepository.updateChallenge(
|
||||
c, taskList, ArrayList(addedTasks.values),
|
||||
c,
|
||||
taskList,
|
||||
ArrayList(addedTasks.values),
|
||||
ArrayList(updatedTasks.values),
|
||||
ArrayList(removedTasks.keys)
|
||||
)
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@ import androidx.lifecycle.lifecycleScope
|
|||
import androidx.navigation.navArgs
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.ActivityClassSelectionBinding
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.user.Gear
|
||||
import com.habitrpg.android.habitica.models.user.Items
|
||||
import com.habitrpg.android.habitica.models.user.Outfit
|
||||
|
|
@ -52,7 +53,7 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
binding.selectedTitleTextView.text = getString(R.string.x_class, className)
|
||||
binding.selectedButton.text = getString(R.string.become_x, className)
|
||||
}
|
||||
private var isInitialSelection: Boolean = false
|
||||
private var isClassSelected: Boolean = false
|
||||
private var classWasUnset: Boolean? = false
|
||||
private var shouldFinish: Boolean? = false
|
||||
|
||||
|
|
@ -74,9 +75,8 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
supportActionBar?.setDisplayShowHomeEnabled(true)
|
||||
|
||||
val args = navArgs<ClassSelectionActivityArgs>().value
|
||||
isInitialSelection = args.isInitialSelection
|
||||
isClassSelected = args.isClassSelected
|
||||
currentClass = args.className
|
||||
|
||||
newClass = currentClass ?: "healer"
|
||||
|
||||
userViewModel.user.observe(this) {
|
||||
|
|
@ -87,15 +87,8 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
if (!isInitialSelection) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.changeClass()
|
||||
classWasUnset
|
||||
}
|
||||
}
|
||||
|
||||
binding.healerWrapper.setOnClickListener { newClass = "healer" }
|
||||
binding.mageWrapper.setOnClickListener { newClass = "wizard" }
|
||||
binding.mageWrapper.setOnClickListener { newClass = "mage" }
|
||||
binding.rogueWrapper.setOnClickListener { newClass = "rogue" }
|
||||
binding.warriorWrapper.setOnClickListener { newClass = "warrior" }
|
||||
binding.selectedButton.setOnClickListener { displayConfirmationDialogForClass() }
|
||||
|
|
@ -212,30 +205,34 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun optOutSelected() {
|
||||
if (!this.isInitialSelection && this.classWasUnset == false) {
|
||||
return
|
||||
}
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setTitle(getString(R.string.opt_out_confirmation))
|
||||
alert.addButton(R.string.opt_out_class, true) { _, _ -> optOutOfClasses() }
|
||||
alert.addButton(R.string.dialog_go_back, false)
|
||||
alert.setMessage(getString(R.string.opt_out_description))
|
||||
alert.addButton(R.string.opt_out_class, true, true) { _, _ ->
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
// Set Player to have no class, and opt out
|
||||
classWasUnset
|
||||
optOutOfClasses()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.close, false)
|
||||
alert.show()
|
||||
}
|
||||
|
||||
private fun displayConfirmationDialogForClass() {
|
||||
if (!this.isInitialSelection && this.classWasUnset == false) {
|
||||
if (isClassSelected) {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setTitle(getString(R.string.change_class_selected_confirmation, newClass))
|
||||
alert.setMessage(getString(R.string.change_class_equipment_warning))
|
||||
alert.setTitle(getString(R.string.change_class_selected_confirmation, className))
|
||||
alert.setMessage(getString(R.string.change_class_confirmation_message))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ ->
|
||||
selectClass(newClass, true)
|
||||
selectClass(newClass)
|
||||
}
|
||||
alert.addButton(R.string.dialog_go_back, false)
|
||||
alert.show()
|
||||
} else {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setTitle(getString(R.string.class_confirmation, className))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ -> selectClass(newClass, false) }
|
||||
alert.addButton(R.string.choose_class, true) { _, _ -> selectClass(newClass) }
|
||||
alert.addButton(R.string.dialog_go_back, false)
|
||||
alert.show()
|
||||
}
|
||||
|
|
@ -244,8 +241,12 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
private fun displayClassChanged(selectedClass: String) {
|
||||
val alert = HabiticaAlertDialog(this)
|
||||
alert.setTitle(getString(R.string.class_changed, className))
|
||||
alert.setMessage(getString(R.string.class_changed_description, selectedClass))
|
||||
alert.addButton(getString(R.string.complete_tutorial), true){ _, _ -> dismiss() }
|
||||
alert.setMessage(getString(R.string.class_changed_description, className))
|
||||
alert.addButton(getString(R.string.complete_tutorial), true) { _, _ -> dismiss() }
|
||||
alert.addButton(getString(R.string.learn_more), false) { _, _ ->
|
||||
dismiss()
|
||||
MainNavigationController.navigate(R.id.FAQOverviewFragment)
|
||||
}
|
||||
alert.setOnCancelListener {
|
||||
dismiss()
|
||||
}
|
||||
|
|
@ -261,17 +262,27 @@ class ClassSelectionActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
private fun selectClass(selectedClass: String, isChanging: Boolean) {
|
||||
private fun selectClass(selectedClass: String) {
|
||||
shouldFinish = true
|
||||
val dialog = this.displayProgressDialog(getString(R.string.changing_class_progress))
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(selectedClass)
|
||||
dialog.hide()
|
||||
if (isChanging) displayClassChanged(selectedClass)
|
||||
val chosenClass = if (selectedClass == "mage") "wizard" else selectedClass
|
||||
if (isClassSelected) {
|
||||
val dialog = this.displayProgressDialog(getString(R.string.changing_class_progress))
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(chosenClass)
|
||||
dialog.hide()
|
||||
displayClassChanged(chosenClass)
|
||||
}
|
||||
} else {
|
||||
val dialog = this.displayProgressDialog(getString(R.string.choosing_class_progress))
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(chosenClass)
|
||||
dialog.hide()
|
||||
displayClassChanged(chosenClass)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayProgressDialog(progressText: String) : HabiticaProgressDialog {
|
||||
private fun displayProgressDialog(progressText: String): HabiticaProgressDialog {
|
||||
return HabiticaProgressDialog.show(this, progressText, 300)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -30,8 +30,10 @@ class DeathActivity : BaseActivity() {
|
|||
|
||||
@Inject
|
||||
internal lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
internal lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
|
|
|
|||
|
|
@ -72,6 +72,7 @@ class FullProfileActivity : BaseActivity() {
|
|||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPrefs: SharedPreferences
|
||||
|
||||
|
|
@ -535,7 +536,11 @@ class FullProfileActivity : BaseActivity() {
|
|||
private fun addLevelAttributes(user: Member) {
|
||||
val byLevelStat = min((user.stats?.lvl ?: 0) / 2.0f, 50f)
|
||||
addAttributeRow(
|
||||
getString(R.string.profile_level), byLevelStat, byLevelStat, byLevelStat, byLevelStat,
|
||||
getString(R.string.profile_level),
|
||||
byLevelStat,
|
||||
byLevelStat,
|
||||
byLevelStat,
|
||||
byLevelStat,
|
||||
roundDown = true,
|
||||
isSummary = false
|
||||
)
|
||||
|
|
@ -684,10 +689,11 @@ class FullProfileActivity : BaseActivity() {
|
|||
binding.attributesCollapseIcon.setImageDrawable(
|
||||
ContextCompat.getDrawable(
|
||||
this,
|
||||
if (attributeDetailsHidden)
|
||||
if (attributeDetailsHidden) {
|
||||
R.drawable.ic_keyboard_arrow_right_black_24dp
|
||||
else
|
||||
} else {
|
||||
R.drawable.ic_keyboard_arrow_down_black_24dp
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
|
|
|
|||
|
|
@ -15,7 +15,6 @@ class GemPurchaseActivity : PurchaseActivity() {
|
|||
return R.layout.activity_gem_purchase
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
|
|
|
|||
|
|
@ -34,8 +34,10 @@ class GiftGemsActivity : PurchaseActivity() {
|
|||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var purchaseHandler: PurchaseHandler
|
||||
|
||||
|
|
|
|||
|
|
@ -28,8 +28,10 @@ class GiftSubscriptionActivity : PurchaseActivity() {
|
|||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var purchaseHandler: PurchaseHandler
|
||||
|
||||
|
|
|
|||
|
|
@ -52,7 +52,6 @@ class GroupInviteActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
|
||||
return when (item.itemId) {
|
||||
R.id.action_send_invites -> {
|
||||
setResult(Activity.RESULT_OK, createResultIntent())
|
||||
|
|
|
|||
|
|
@ -49,6 +49,8 @@ class GuidelinesActivity : BaseActivity() {
|
|||
return if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
} else {
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -26,6 +26,7 @@ import javax.inject.Inject
|
|||
class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChangeListener {
|
||||
|
||||
private lateinit var binding: ActivityIntroBinding
|
||||
|
||||
@Inject
|
||||
lateinit var contentRepository: ContentRepository
|
||||
|
||||
|
|
|
|||
|
|
@ -57,12 +57,15 @@ class LoginActivity : BaseActivity() {
|
|||
|
||||
@Inject
|
||||
lateinit var apiClient: ApiClient
|
||||
|
||||
@Inject
|
||||
lateinit var sharedPrefs: SharedPreferences
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var viewModel : AuthenticationViewModel
|
||||
lateinit var viewModel: AuthenticationViewModel
|
||||
|
||||
private var isRegistering: Boolean = false
|
||||
private var isShowingForm: Boolean = false
|
||||
|
|
@ -171,7 +174,8 @@ class LoginActivity : BaseActivity() {
|
|||
binding.forgotPassword.setOnClickListener { onForgotPasswordClicked() }
|
||||
binding.googleLoginButton.setOnClickListener {
|
||||
binding.googleLoginProgress.visibility = View.VISIBLE
|
||||
viewModel.handleGoogleLogin(this, pickAccountResult) }
|
||||
viewModel.handleGoogleLogin(this, pickAccountResult)
|
||||
}
|
||||
}
|
||||
|
||||
override fun loadTheme(sharedPreferences: SharedPreferences, forced: Boolean) {
|
||||
|
|
|
|||
|
|
@ -41,8 +41,10 @@ import com.habitrpg.android.habitica.extensions.hideKeyboard
|
|||
import com.habitrpg.android.habitica.extensions.observeOnce
|
||||
import com.habitrpg.android.habitica.extensions.setScaledPadding
|
||||
import com.habitrpg.android.habitica.extensions.updateStatusBarColor
|
||||
import com.habitrpg.android.habitica.helpers.AmplitudeManager
|
||||
import com.habitrpg.android.habitica.helpers.Analytics
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.EventCategory
|
||||
import com.habitrpg.android.habitica.helpers.HitType
|
||||
import com.habitrpg.android.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.helpers.NotificationOpenHandler
|
||||
import com.habitrpg.android.habitica.helpers.SoundManager
|
||||
|
|
@ -157,6 +159,13 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
}
|
||||
}
|
||||
|
||||
private val classSelectionResult =
|
||||
registerForActivityResult(ActivityResultContracts.StartActivityForResult()) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
userRepository.retrieveUser(true, true)
|
||||
}
|
||||
}
|
||||
|
||||
val isAppBarExpanded: Boolean
|
||||
get() = binding.content.appbar.height - binding.content.appbar.bottom == 0
|
||||
|
||||
|
|
@ -265,19 +274,36 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
binding.content.headerView.setContent {
|
||||
HabiticaTheme {
|
||||
val user by viewModel.user.observeAsState(null)
|
||||
val teamPlan by viewModel.userViewModel.currentTeamPlan.collectAsStateLifecycleAware(null)
|
||||
val teamPlan by viewModel.userViewModel.currentTeamPlan.collectAsStateLifecycleAware(
|
||||
null
|
||||
)
|
||||
val teamPlanMembers by viewModel.userViewModel.currentTeamPlanMembers.observeAsState()
|
||||
val canShowTeamHeader: Boolean by viewModel.canShowTeamPlanHeader
|
||||
AppHeaderView(user, teamPlan = if (canShowTeamHeader) teamPlan else null, teamPlanMembers = teamPlanMembers) {
|
||||
showAsBottomSheet { onClose ->
|
||||
val group by viewModel.userViewModel.currentTeamPlanGroup.collectAsState(null)
|
||||
val members by viewModel.userViewModel.currentTeamPlanMembers.observeAsState()
|
||||
GroupPlanMemberList(members, group) {
|
||||
onClose()
|
||||
FullProfileActivity.open(it)
|
||||
AppHeaderView(
|
||||
user,
|
||||
teamPlan = if (canShowTeamHeader) teamPlan else null,
|
||||
teamPlanMembers = teamPlanMembers,
|
||||
onMemberRowClicked = {
|
||||
showAsBottomSheet { onClose ->
|
||||
val group by viewModel.userViewModel.currentTeamPlanGroup.collectAsState(
|
||||
null
|
||||
)
|
||||
val members by viewModel.userViewModel.currentTeamPlanMembers.observeAsState()
|
||||
GroupPlanMemberList(members, group) {
|
||||
onClose()
|
||||
FullProfileActivity.open(it)
|
||||
}
|
||||
}
|
||||
},
|
||||
onClassSelectionClicked = {
|
||||
val bundle = Bundle()
|
||||
val isClassSelected = user?.flags?.classSelected ?: false
|
||||
bundle.putBoolean("isInitialSelection", isClassSelected)
|
||||
val intent = Intent(this@MainActivity, ClassSelectionActivity::class.java)
|
||||
intent.putExtras(bundle)
|
||||
classSelectionResult.launch(intent)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -356,7 +382,9 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
MainNavigationController.navigateBack()
|
||||
}
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
} else {
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
|
|
@ -404,10 +432,10 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
if (intent.hasExtra("sendAnalytics")) {
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["identifier"] = identifier
|
||||
AmplitudeManager.sendEvent(
|
||||
Analytics.sendEvent(
|
||||
"open notification",
|
||||
AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR,
|
||||
AmplitudeManager.EVENT_HITTYPE_EVENT,
|
||||
EventCategory.BEHAVIOUR,
|
||||
HitType.EVENT,
|
||||
additionalData
|
||||
)
|
||||
}
|
||||
|
|
@ -681,7 +709,7 @@ open class MainActivity : BaseActivity(), SnackbarActivity {
|
|||
}
|
||||
}
|
||||
|
||||
fun updateToolbarInteractivity(titleInteractive : Boolean) {
|
||||
fun updateToolbarInteractivity(titleInteractive: Boolean) {
|
||||
viewModel.canShowTeamPlanHeader.value = titleInteractive
|
||||
binding.content.toolbarTitle.background?.alpha = if (titleInteractive) 255 else 0
|
||||
if (titleInteractive) {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ import com.habitrpg.android.habitica.data.InventoryRepository
|
|||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
import com.habitrpg.android.habitica.databinding.ActivityNotificationsBinding
|
||||
import com.habitrpg.android.habitica.extensions.fadeInAnimation
|
||||
import com.habitrpg.android.habitica.extensions.observeOnce
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestContent
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
|
||||
import com.habitrpg.common.habitica.extensions.fromHtml
|
||||
|
|
@ -48,15 +49,17 @@ import javax.inject.Inject
|
|||
class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
private lateinit var binding: ActivityNotificationsBinding
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
val viewModel: NotificationsViewModel by viewModels()
|
||||
|
||||
var inflater: LayoutInflater? = null
|
||||
val viewTagMap = mutableMapOf<String, View>()
|
||||
var userLvl: Int? = null
|
||||
|
||||
override fun getLayoutResId(): Int = R.layout.activity_notifications
|
||||
|
||||
|
|
@ -72,6 +75,12 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
|
||||
setupToolbar(binding.toolbar)
|
||||
|
||||
// Check user level to handle if a user loses hp and drops below necessary level to allocate points -
|
||||
// and if so, don't display the notification to allocate points.
|
||||
viewModel.user.observeOnce(this) { user ->
|
||||
userLvl = user?.stats?.lvl ?: 0
|
||||
}
|
||||
|
||||
inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
|
|
@ -146,12 +155,11 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
viewList.add(item)
|
||||
}
|
||||
}
|
||||
refreshViews(viewList)
|
||||
updateNotificationsAndRefresh(viewList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun refreshViews(newItems: List<View>) {
|
||||
private fun updateNotificationsAndRefresh(newItems: List<View>) {
|
||||
val currentViews = (0 until binding.notificationItems.childCount).map {
|
||||
binding.notificationItems.getChildAt(it)
|
||||
}
|
||||
|
|
@ -171,6 +179,17 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
}
|
||||
}
|
||||
|
||||
private fun removeNotificationAndRefresh(notification: Notification) {
|
||||
// Immediately remove notification for better user experience
|
||||
// (To avoid waiting for the server to respond for potential slower connections)
|
||||
this.notifications = this.notifications.filter { it.id != notification.id }
|
||||
|
||||
if (notifications.isEmpty()) {
|
||||
displayNoNotificationsView()
|
||||
} else {
|
||||
displayNotificationsListView(notifications)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createNotificationsHeaderView(notificationCount: Int): View? {
|
||||
val header = inflater?.inflate(R.layout.notifications_header, binding.notificationItems, false)
|
||||
|
|
@ -203,29 +222,38 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
)
|
||||
}
|
||||
|
||||
private fun createNewStuffNotification(notification: Notification): View? {
|
||||
private suspend fun createNewStuffNotification(notification: Notification): View? = withContext(ExceptionHandler.coroutine()) {
|
||||
var baileyNotification = notification
|
||||
val data = notification.data as? NewStuffData
|
||||
val text = if (data?.title != null) {
|
||||
fromHtml("<b>" + getString(R.string.new_bailey_update) + "</b><br>" + data.title)
|
||||
} else {
|
||||
fromHtml("<b>" + getString(R.string.new_bailey_update) + "</b>")
|
||||
baileyNotification = userRepository.getNewsNotification() ?: return@withContext null
|
||||
val baileyNewsData = baileyNotification.data as? NewStuffData
|
||||
fromHtml("<b>" + getString(R.string.new_bailey_update) + "</b><br>" + baileyNewsData?.title)
|
||||
}
|
||||
baileyNotification.id = notification.id
|
||||
|
||||
return createDismissableNotificationItem(
|
||||
notification,
|
||||
return@withContext createDismissableNotificationItem(
|
||||
baileyNotification,
|
||||
text,
|
||||
R.drawable.notifications_bailey
|
||||
)
|
||||
}
|
||||
|
||||
private fun createUnallocatedStatsNotification(notification: Notification): View? {
|
||||
val data = notification.data as? UnallocatedPointsData
|
||||
val level = userLvl ?: return null
|
||||
return if (level >= 10) {
|
||||
val data = notification.data as? UnallocatedPointsData
|
||||
|
||||
return createDismissableNotificationItem(
|
||||
notification,
|
||||
fromHtml(getString(R.string.unallocated_stats_points, data?.points.toString())),
|
||||
R.drawable.notification_stat_sparkles
|
||||
)
|
||||
createDismissableNotificationItem(
|
||||
notification,
|
||||
fromHtml(getString(R.string.unallocated_stats_points, data?.points.toString())),
|
||||
R.drawable.notification_stat_sparkles
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
private fun createMysteryItemsNotification(notification: Notification): View? {
|
||||
|
|
@ -304,7 +332,10 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
}
|
||||
|
||||
val dismissButton = item?.findViewById(R.id.dismiss_button) as? ImageView
|
||||
dismissButton?.setOnClickListener { viewModel.dismissNotification(notification) }
|
||||
dismissButton?.setOnClickListener {
|
||||
removeNotificationAndRefresh(notification)
|
||||
viewModel.dismissNotification(notification)
|
||||
}
|
||||
|
||||
val messageTextView = item?.findViewById(R.id.message_text) as? TextView
|
||||
messageTextView?.text = messageText
|
||||
|
|
@ -327,7 +358,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
|
|||
|
||||
return item
|
||||
}
|
||||
|
||||
|
||||
private suspend fun createPartyInvitationNotification(notification: Notification): View? = withContext(ExceptionHandler.coroutine()) {
|
||||
val data = notification.data as? PartyInvitationData
|
||||
val inviterId = data?.invitation?.inviter
|
||||
|
|
|
|||
|
|
@ -46,8 +46,10 @@ class SetupActivity : BaseActivity(), ViewPager.OnPageChangeListener {
|
|||
|
||||
@Inject
|
||||
lateinit var apiClient: ApiClient
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var taskRepository: TaskRepository
|
||||
|
||||
|
|
|
|||
|
|
@ -27,6 +27,7 @@ class SkillMemberActivity : BaseActivity() {
|
|||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
|
|
|
|||
|
|
@ -13,7 +13,6 @@ import com.habitrpg.android.habitica.R
|
|||
import com.habitrpg.android.habitica.data.TaskRepository
|
||||
import com.habitrpg.android.habitica.databinding.ActivitySkillTasksBinding
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.fragments.skills.SkillTasksRecyclerViewFragment
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
|
|
@ -87,6 +86,8 @@ class SkillTasksActivity : BaseActivity() {
|
|||
return if (item.itemId == android.R.id.home) {
|
||||
onBackPressed()
|
||||
true
|
||||
} else super.onOptionsItemSelected(item)
|
||||
} else {
|
||||
super.onOptionsItemSelected(item)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -249,7 +249,6 @@ class TaskFormActivity : BaseActivity() {
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
title = ""
|
||||
when {
|
||||
taskId != null -> {
|
||||
|
|
@ -365,7 +364,8 @@ class TaskFormActivity : BaseActivity() {
|
|||
LabeledValue(getString(R.string.weekly), HabitResetOption.WEEKLY),
|
||||
LabeledValue(getString(R.string.monthly), HabitResetOption.MONTHLY)
|
||||
),
|
||||
{ viewModel.habitResetOption.value = it }, columnSize = 3
|
||||
{ viewModel.habitResetOption.value = it },
|
||||
columnSize = 3
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -648,10 +648,14 @@ class TaskFormActivity : BaseActivity() {
|
|||
thisTask.up = viewModel.habitScoringPositive.value
|
||||
thisTask.down = viewModel.habitScoringNegative.value
|
||||
thisTask.frequency = viewModel.habitResetOption.value.value
|
||||
if (binding.habitAdjustPositiveStreakView.text?.isNotEmpty() == true) thisTask.counterUp =
|
||||
binding.habitAdjustPositiveStreakView.text.toString().toIntCatchOverflow()
|
||||
if (binding.habitAdjustNegativeStreakView.text?.isNotEmpty() == true) thisTask.counterDown =
|
||||
binding.habitAdjustNegativeStreakView.text.toString().toIntCatchOverflow()
|
||||
if (binding.habitAdjustPositiveStreakView.text?.isNotEmpty() == true) {
|
||||
thisTask.counterUp =
|
||||
binding.habitAdjustPositiveStreakView.text.toString().toIntCatchOverflow()
|
||||
}
|
||||
if (binding.habitAdjustNegativeStreakView.text?.isNotEmpty() == true) {
|
||||
thisTask.counterDown =
|
||||
binding.habitAdjustNegativeStreakView.text.toString().toIntCatchOverflow()
|
||||
}
|
||||
} else if (taskType == TaskType.DAILY) {
|
||||
thisTask.startDate = binding.taskSchedulingControls.startDate
|
||||
thisTask.everyX = binding.taskSchedulingControls.everyX
|
||||
|
|
@ -659,8 +663,10 @@ class TaskFormActivity : BaseActivity() {
|
|||
thisTask.repeat = binding.taskSchedulingControls.weeklyRepeat
|
||||
thisTask.setDaysOfMonth(binding.taskSchedulingControls.daysOfMonth)
|
||||
thisTask.setWeeksOfMonth(binding.taskSchedulingControls.weeksOfMonth)
|
||||
if (binding.habitAdjustPositiveStreakView.text?.isNotEmpty() == true) thisTask.streak =
|
||||
binding.habitAdjustPositiveStreakView.text.toString().toIntCatchOverflow()
|
||||
if (binding.habitAdjustPositiveStreakView.text?.isNotEmpty() == true) {
|
||||
thisTask.streak =
|
||||
binding.habitAdjustPositiveStreakView.text.toString().toIntCatchOverflow()
|
||||
}
|
||||
checkIfShowNotifLayout()
|
||||
} else if (taskType == TaskType.TODO) {
|
||||
thisTask.dueDate = binding.taskSchedulingControls.dueDate
|
||||
|
|
|
|||
|
|
@ -67,27 +67,27 @@ import javax.inject.Inject
|
|||
@HiltViewModel
|
||||
class TaskSummaryViewModel @Inject constructor(
|
||||
savedStateHandle: SavedStateHandle,
|
||||
userRepository : UserRepository,
|
||||
userViewModel : MainUserViewModel,
|
||||
val taskRepository : TaskRepository,
|
||||
val socialRepository : SocialRepository
|
||||
userRepository: UserRepository,
|
||||
userViewModel: MainUserViewModel,
|
||||
val taskRepository: TaskRepository,
|
||||
val socialRepository: SocialRepository
|
||||
) : BaseViewModel(userRepository, userViewModel) {
|
||||
val taskID: String = savedStateHandle[TaskFormActivity.TASK_ID_KEY] ?: ""
|
||||
|
||||
val task = taskRepository.getTask(taskID).asLiveData()
|
||||
|
||||
fun getMember(userID : String?) : Flow<Member?> {
|
||||
fun getMember(userID: String?): Flow<Member?> {
|
||||
return socialRepository.getMember(userID)
|
||||
}
|
||||
}
|
||||
|
||||
@AndroidEntryPoint
|
||||
class TaskSummaryActivity : BaseActivity() {
|
||||
override fun getLayoutResId() : Int? = null
|
||||
override fun getLayoutResId(): Int? = null
|
||||
|
||||
private val viewModel : TaskSummaryViewModel by viewModels()
|
||||
private val viewModel: TaskSummaryViewModel by viewModels()
|
||||
|
||||
override fun onCreate(savedInstanceState : Bundle?) {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setContent {
|
||||
HabiticaTheme {
|
||||
|
|
@ -98,7 +98,7 @@ class TaskSummaryActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
@Composable
|
||||
fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
||||
fun TaskSummaryView(viewModel: TaskSummaryViewModel) {
|
||||
val taskDescriptionBuilder = TaskDescriptionBuilder(LocalContext.current)
|
||||
val task by viewModel.task.observeAsState()
|
||||
val titleModifier = Modifier.padding(top = 30.dp)
|
||||
|
|
@ -107,9 +107,13 @@ fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
|||
|
||||
if (task != null) {
|
||||
val darkestColor = HabiticaTheme.colors.textPrimaryFor(task)
|
||||
val topTextColor = if ((task?.value ?: 0.0) >= -20) colorResource(
|
||||
task?.extraDarkTaskColor ?: R.color.white
|
||||
) else Color.White
|
||||
val topTextColor = if ((task?.value ?: 0.0) >= -20) {
|
||||
colorResource(
|
||||
task?.extraDarkTaskColor ?: R.color.white
|
||||
)
|
||||
} else {
|
||||
Color.White
|
||||
}
|
||||
val systemUiController = rememberSystemUiController()
|
||||
val statusBarColor = HabiticaTheme.colors.primaryBackgroundFor(task)
|
||||
val lightestColor = HabiticaTheme.colors.contentBackgroundFor(task)
|
||||
|
|
@ -169,8 +173,11 @@ fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
|||
modifier = titleModifier
|
||||
)
|
||||
Text(
|
||||
task?.text ?: "", fontSize = 16.sp, color = darkestColor,
|
||||
fontWeight = FontWeight.Normal, modifier = textModifier
|
||||
task?.text ?: "",
|
||||
fontSize = 16.sp,
|
||||
color = darkestColor,
|
||||
fontWeight = FontWeight.Normal,
|
||||
modifier = textModifier
|
||||
)
|
||||
if (task?.notes?.isNotBlank() == true) {
|
||||
Text(
|
||||
|
|
@ -270,7 +277,8 @@ fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
|||
for (item in task?.group?.assignedUsersDetail ?: emptyList()) {
|
||||
val member = viewModel.getMember(item.assignedUserID).collectAsState(null)
|
||||
UserRow(
|
||||
item.assignedUsername ?: "", member.value,
|
||||
item.assignedUsername ?: "",
|
||||
member.value,
|
||||
Modifier
|
||||
.padding(vertical = 4.dp)
|
||||
.background(
|
||||
|
|
@ -281,11 +289,15 @@ fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
|||
.heightIn(min = 24.dp)
|
||||
.fillMaxWidth(),
|
||||
color = darkestColor,
|
||||
extraContent = if (item.completed) (
|
||||
{
|
||||
CompletedAt(item.completedDate)
|
||||
}
|
||||
) else null
|
||||
extraContent = if (item.completed) {
|
||||
(
|
||||
{
|
||||
CompletedAt(item.completedDate)
|
||||
}
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
task?.group?.assignedUsersDetail?.find { it.assignedUserID == viewModel.userViewModel.userID }
|
||||
|
|
@ -311,7 +323,7 @@ fun TaskSummaryView(viewModel : TaskSummaryViewModel) {
|
|||
}
|
||||
}
|
||||
|
||||
private fun String.makeBoldComposable() : AnnotatedString {
|
||||
private fun String.makeBoldComposable(): AnnotatedString {
|
||||
return buildAnnotatedString {
|
||||
var isBold = false
|
||||
for (segment in split("**")) {
|
||||
|
|
|
|||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Reference in a new issue