mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-22 21:57:11 +00:00
format fixes
This commit is contained in:
parent
ce28bf83f3
commit
bf62f5ac16
30 changed files with 4481 additions and 3777 deletions
|
|
@ -52,7 +52,7 @@ interface ApiService {
|
|||
@GET("status")
|
||||
suspend fun getStatus(): HabitResponse<Status>
|
||||
|
||||
/* user API */
|
||||
// user API
|
||||
|
||||
@GET("user/")
|
||||
suspend fun getUser(): HabitResponse<User>
|
||||
|
|
@ -61,7 +61,10 @@ interface ApiService {
|
|||
suspend fun syncUserStats(): HabitResponse<User>
|
||||
|
||||
@GET("inbox/messages")
|
||||
suspend fun getInboxMessages(@Query("conversation") uuid: String, @Query("page") page: Int): HabitResponse<List<ChatMessage>>
|
||||
suspend fun getInboxMessages(
|
||||
@Query("conversation") uuid: String,
|
||||
@Query("page") page: Int,
|
||||
): HabitResponse<List<ChatMessage>>
|
||||
|
||||
@GET("inbox/conversations")
|
||||
suspend fun getInboxConversations(): HabitResponse<List<InboxConversation>>
|
||||
|
|
@ -73,113 +76,192 @@ interface ApiService {
|
|||
suspend fun worldState(): HabitResponse<WorldState>
|
||||
|
||||
@GET("content")
|
||||
suspend fun getContent(@Query("language") language: String?): HabitResponse<ContentResult>
|
||||
suspend fun getContent(
|
||||
@Query("language") language: String?,
|
||||
): HabitResponse<ContentResult>
|
||||
|
||||
@PUT("user/")
|
||||
suspend fun updateUser(@Body updateDictionary: Map<String, Any?>): HabitResponse<User>
|
||||
suspend fun updateUser(
|
||||
@Body updateDictionary: Map<String, Any?>,
|
||||
): HabitResponse<User>
|
||||
|
||||
@PUT("user/")
|
||||
suspend fun registrationLanguage(@Header("Accept-Language") registrationLanguage: String): HabitResponse<User>
|
||||
suspend fun registrationLanguage(
|
||||
@Header("Accept-Language") registrationLanguage: String,
|
||||
): HabitResponse<User>
|
||||
|
||||
@GET("user/in-app-rewards")
|
||||
suspend fun retrieveInAppRewards(): HabitResponse<List<ShopItem>>
|
||||
|
||||
@POST("user/equip/{type}/{key}")
|
||||
suspend fun equipItem(@Path("type") type: String, @Path("key") itemKey: String): HabitResponse<Items>
|
||||
suspend fun equipItem(
|
||||
@Path("type") type: String,
|
||||
@Path("key") itemKey: String,
|
||||
): HabitResponse<Items>
|
||||
|
||||
@POST("user/buy/{key}")
|
||||
suspend fun buyItem(@Path("key") itemKey: String, @Body quantity: Map<String, Int>): HabitResponse<BuyResponse>
|
||||
suspend fun buyItem(
|
||||
@Path("key") itemKey: String,
|
||||
@Body quantity: Map<String, Int>,
|
||||
): HabitResponse<BuyResponse>
|
||||
|
||||
@POST("user/purchase/{type}/{key}")
|
||||
suspend fun purchaseItem(
|
||||
@Path("type") type: String,
|
||||
@Path("key") itemKey: String,
|
||||
@Body quantity: Map<String, Int>
|
||||
@Body quantity: Map<String, Int>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/purchase-hourglass/{type}/{key}")
|
||||
suspend fun purchaseHourglassItem(@Path("type") type: String, @Path("key") itemKey: String): HabitResponse<Void>
|
||||
suspend fun purchaseHourglassItem(
|
||||
@Path("type") type: String,
|
||||
@Path("key") itemKey: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/buy-mystery-set/{key}")
|
||||
suspend fun purchaseMysterySet(@Path("key") itemKey: String): HabitResponse<Void>
|
||||
suspend fun purchaseMysterySet(
|
||||
@Path("key") itemKey: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/buy-quest/{key}")
|
||||
suspend fun purchaseQuest(@Path("key") key: String): HabitResponse<Void>
|
||||
suspend fun purchaseQuest(
|
||||
@Path("key") key: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/buy-special-spell/{key}")
|
||||
suspend fun purchaseSpecialSpell(@Path("key") key: String): HabitResponse<Void>
|
||||
suspend fun purchaseSpecialSpell(
|
||||
@Path("key") key: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/sell/{type}/{key}")
|
||||
suspend fun sellItem(@Path("type") itemType: String, @Path("key") itemKey: String): HabitResponse<User>
|
||||
suspend fun sellItem(
|
||||
@Path("type") itemType: String,
|
||||
@Path("key") itemKey: String,
|
||||
): HabitResponse<User>
|
||||
|
||||
@POST("user/feed/{pet}/{food}")
|
||||
suspend fun feedPet(@Path("pet") petKey: String, @Path("food") foodKey: String): HabitResponse<FeedResponse>
|
||||
suspend fun feedPet(
|
||||
@Path("pet") petKey: String,
|
||||
@Path("food") foodKey: String,
|
||||
): HabitResponse<FeedResponse>
|
||||
|
||||
@POST("user/hatch/{egg}/{hatchingPotion}")
|
||||
suspend fun hatchPet(@Path("egg") eggKey: String, @Path("hatchingPotion") hatchingPotionKey: String): HabitResponse<Items>
|
||||
suspend fun hatchPet(
|
||||
@Path("egg") eggKey: String,
|
||||
@Path("hatchingPotion") hatchingPotionKey: String,
|
||||
): HabitResponse<Items>
|
||||
|
||||
@GET("tasks/user")
|
||||
suspend fun getTasks(@Query("type") type: String): HabitResponse<TaskList>
|
||||
suspend fun getTasks(
|
||||
@Query("type") type: String,
|
||||
): HabitResponse<TaskList>
|
||||
|
||||
@GET("tasks/user")
|
||||
suspend fun getTasks(@Query("type") type: String, @Query("dueDate") dueDate: String): HabitResponse<TaskList>
|
||||
suspend fun getTasks(
|
||||
@Query("type") type: String,
|
||||
@Query("dueDate") dueDate: String,
|
||||
): HabitResponse<TaskList>
|
||||
|
||||
@POST("user/unlock")
|
||||
suspend fun unlockPath(@Query("path") path: String): HabitResponse<UnlockResponse>
|
||||
suspend fun unlockPath(
|
||||
@Query("path") path: String,
|
||||
): HabitResponse<UnlockResponse>
|
||||
|
||||
@GET("tasks/{id}")
|
||||
suspend fun getTask(@Path("id") id: String): HabitResponse<Task>
|
||||
suspend fun getTask(
|
||||
@Path("id") id: String,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/{id}/score/{direction}")
|
||||
suspend fun postTaskDirection(@Path("id") id: String, @Path("direction") direction: String): HabitResponse<TaskDirectionData>
|
||||
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>
|
||||
suspend fun bulkScoreTasks(
|
||||
@Body data: List<Map<String, String>>,
|
||||
): HabitResponse<BulkTaskScoringData>
|
||||
|
||||
@POST("tasks/{id}/move/to/{position}")
|
||||
suspend fun postTaskNewPosition(@Path("id") id: String, @Path("position") position: Int): HabitResponse<List<String>>
|
||||
suspend fun postTaskNewPosition(
|
||||
@Path("id") id: String,
|
||||
@Path("position") position: Int,
|
||||
): HabitResponse<List<String>>
|
||||
|
||||
@POST("tasks/{taskId}/checklist/{itemId}/score")
|
||||
suspend fun scoreChecklistItem(@Path("taskId") taskId: String, @Path("itemId") itemId: String): HabitResponse<Task>
|
||||
suspend fun scoreChecklistItem(
|
||||
@Path("taskId") taskId: String,
|
||||
@Path("itemId") itemId: String,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/user")
|
||||
suspend fun createTask(@Body item: Task): HabitResponse<Task>
|
||||
suspend fun createTask(
|
||||
@Body item: Task,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/group/{groupId}")
|
||||
suspend fun createGroupTask(@Path("groupId") groupId: String, @Body item: Task): HabitResponse<Task>
|
||||
suspend fun createGroupTask(
|
||||
@Path("groupId") groupId: String,
|
||||
@Body item: Task,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/user")
|
||||
suspend fun createTasks(@Body tasks: List<Task>): HabitResponse<List<Task>>
|
||||
suspend fun createTasks(
|
||||
@Body tasks: List<Task>,
|
||||
): HabitResponse<List<Task>>
|
||||
|
||||
@PUT("tasks/{id}")
|
||||
suspend fun updateTask(@Path("id") id: String, @Body item: Task): HabitResponse<Task>
|
||||
suspend fun updateTask(
|
||||
@Path("id") id: String,
|
||||
@Body item: Task,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@DELETE("tasks/{id}")
|
||||
suspend fun deleteTask(@Path("id") id: String): HabitResponse<Void>
|
||||
suspend fun deleteTask(
|
||||
@Path("id") id: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("tags")
|
||||
suspend fun createTag(@Body tag: Tag): HabitResponse<Tag>
|
||||
suspend fun createTag(
|
||||
@Body tag: Tag,
|
||||
): HabitResponse<Tag>
|
||||
|
||||
@PUT("tags/{id}")
|
||||
suspend fun updateTag(@Path("id") id: String, @Body tag: Tag): HabitResponse<Tag>
|
||||
suspend fun updateTag(
|
||||
@Path("id") id: String,
|
||||
@Body tag: Tag,
|
||||
): HabitResponse<Tag>
|
||||
|
||||
@DELETE("tags/{id}")
|
||||
suspend fun deleteTag(@Path("id") id: String): HabitResponse<Void>
|
||||
suspend fun deleteTag(
|
||||
@Path("id") id: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/auth/local/register")
|
||||
suspend fun registerUser(@Body auth: UserAuth): HabitResponse<UserAuthResponse>
|
||||
suspend fun registerUser(
|
||||
@Body auth: UserAuth,
|
||||
): HabitResponse<UserAuthResponse>
|
||||
|
||||
@POST("user/auth/local/login")
|
||||
suspend fun connectLocal(@Body auth: UserAuth): HabitResponse<UserAuthResponse>
|
||||
suspend fun connectLocal(
|
||||
@Body auth: UserAuth,
|
||||
): HabitResponse<UserAuthResponse>
|
||||
|
||||
@POST("user/auth/social")
|
||||
suspend fun connectSocial(@Body auth: UserAuthSocial): HabitResponse<UserAuthResponse>
|
||||
suspend fun connectSocial(
|
||||
@Body auth: UserAuthSocial,
|
||||
): HabitResponse<UserAuthResponse>
|
||||
|
||||
@DELETE("user/auth/social/{network}")
|
||||
suspend fun disconnectSocial(@Path("network") network: String): HabitResponse<Void>
|
||||
suspend fun disconnectSocial(
|
||||
@Path("network") network: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/auth/apple")
|
||||
suspend fun loginApple(@Body auth: Map<String, Any>): HabitResponse<UserAuthResponse>
|
||||
suspend fun loginApple(
|
||||
@Body auth: Map<String, Any>,
|
||||
): HabitResponse<UserAuthResponse>
|
||||
|
||||
@POST("user/sleep")
|
||||
suspend fun sleep(): HabitResponse<Boolean>
|
||||
|
|
@ -191,17 +273,22 @@ interface ApiService {
|
|||
suspend fun useSkill(
|
||||
@Path("skill") skillName: String,
|
||||
@Query("targetType") targetType: String,
|
||||
@Query("targetId") targetId: String
|
||||
@Query("targetId") targetId: String,
|
||||
): HabitResponse<SkillResponse>
|
||||
|
||||
@POST("user/class/cast/{skill}")
|
||||
suspend fun useSkill(@Path("skill") skillName: String, @Query("targetType") targetType: String): HabitResponse<SkillResponse>
|
||||
suspend fun useSkill(
|
||||
@Path("skill") skillName: String,
|
||||
@Query("targetType") targetType: String,
|
||||
): HabitResponse<SkillResponse>
|
||||
|
||||
@POST("user/change-class")
|
||||
suspend fun changeClass(): HabitResponse<User>
|
||||
|
||||
@POST("user/change-class")
|
||||
suspend fun changeClass(@Query("class") className: String): HabitResponse<User>
|
||||
suspend fun changeClass(
|
||||
@Query("class") className: String,
|
||||
): HabitResponse<User>
|
||||
|
||||
@POST("user/disable-classes")
|
||||
suspend fun disableClasses(): HabitResponse<User>
|
||||
|
|
@ -209,189 +296,301 @@ interface ApiService {
|
|||
@POST("user/mark-pms-read")
|
||||
suspend fun markPrivateMessagesRead(): Void?
|
||||
|
||||
/* Group API */
|
||||
// Group API
|
||||
|
||||
@GET("groups")
|
||||
suspend fun listGroups(@Query("type") type: String): HabitResponse<List<Group>>
|
||||
suspend fun listGroups(
|
||||
@Query("type") type: String,
|
||||
): HabitResponse<List<Group>>
|
||||
|
||||
@GET("groups/{gid}")
|
||||
suspend fun getGroup(@Path("gid") groupId: String): HabitResponse<Group>
|
||||
suspend fun getGroup(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Group>
|
||||
|
||||
@POST("groups")
|
||||
suspend fun createGroup(@Body item: Group): HabitResponse<Group>
|
||||
suspend fun createGroup(
|
||||
@Body item: Group,
|
||||
): HabitResponse<Group>
|
||||
|
||||
@PUT("groups/{id}")
|
||||
suspend fun updateGroup(@Path("id") id: String, @Body item: Group): HabitResponse<Group>
|
||||
suspend fun updateGroup(
|
||||
@Path("id") id: String,
|
||||
@Body item: Group,
|
||||
): HabitResponse<Group>
|
||||
|
||||
@POST("groups/{groupID}/removeMember/{userID}")
|
||||
suspend fun removeMemberFromGroup(@Path("groupID") groupID: String, @Path("userID") userID: String): HabitResponse<Void>
|
||||
suspend fun removeMemberFromGroup(
|
||||
@Path("groupID") groupID: String,
|
||||
@Path("userID") userID: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@GET("groups/{gid}/chat")
|
||||
suspend fun listGroupChat(@Path("gid") groupId: String): HabitResponse<List<ChatMessage>>
|
||||
suspend fun listGroupChat(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<List<ChatMessage>>
|
||||
|
||||
@POST("groups/{gid}/join")
|
||||
suspend fun joinGroup(@Path("gid") groupId: String): HabitResponse<Group>
|
||||
suspend fun joinGroup(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Group>
|
||||
|
||||
@POST("groups/{gid}/leave")
|
||||
suspend fun leaveGroup(@Path("gid") groupId: String, @Query("keepChallenges") keepChallenges: String): HabitResponse<Void>
|
||||
suspend fun leaveGroup(
|
||||
@Path("gid") groupId: String,
|
||||
@Query("keepChallenges") keepChallenges: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/chat")
|
||||
suspend fun postGroupChat(@Path("gid") groupId: String, @Body message: Map<String, String>): HabitResponse<PostChatMessageResult>
|
||||
suspend fun postGroupChat(
|
||||
@Path("gid") groupId: String,
|
||||
@Body message: Map<String, String>,
|
||||
): HabitResponse<PostChatMessageResult>
|
||||
|
||||
@DELETE("groups/{gid}/chat/{messageId}")
|
||||
suspend fun deleteMessage(@Path("gid") groupId: String, @Path("messageId") messageId: String): HabitResponse<Void>
|
||||
suspend fun deleteMessage(
|
||||
@Path("gid") groupId: String,
|
||||
@Path("messageId") messageId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@DELETE("inbox/messages/{messageId}")
|
||||
suspend fun deleteInboxMessage(@Path("messageId") messageId: String): HabitResponse<Void>
|
||||
suspend fun deleteInboxMessage(
|
||||
@Path("messageId") messageId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@GET("groups/{gid}/members")
|
||||
suspend fun getGroupMembers(
|
||||
@Path("gid") groupId: String,
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?,
|
||||
): HabitResponse<List<Member>>
|
||||
|
||||
@GET("groups/{gid}/members")
|
||||
suspend fun getGroupMembers(
|
||||
@Path("gid") groupId: String,
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?,
|
||||
@Query("lastId") lastId: String
|
||||
@Query("lastId") lastId: String,
|
||||
): HabitResponse<List<Member>>
|
||||
|
||||
// Like returns the full chat list
|
||||
@POST("groups/{gid}/chat/{mid}/like")
|
||||
suspend fun likeMessage(@Path("gid") groupId: String, @Path("mid") mid: String): HabitResponse<ChatMessage>
|
||||
suspend fun likeMessage(
|
||||
@Path("gid") groupId: String,
|
||||
@Path("mid") mid: String,
|
||||
): HabitResponse<ChatMessage>
|
||||
|
||||
@POST("groups/{gid}/chat/{mid}/flag")
|
||||
suspend fun flagMessage(
|
||||
@Path("gid") groupId: String,
|
||||
@Path("mid") mid: String,
|
||||
@Body data: Map<String, String>
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("members/{mid}/flag")
|
||||
suspend fun reportMember(@Path("mid") mid: String, @Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun reportMember(
|
||||
@Path("mid") mid: String,
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/chat/seen")
|
||||
suspend fun seenMessages(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun seenMessages(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/invite")
|
||||
suspend fun inviteToGroup(@Path("gid") groupId: String, @Body inviteData: Map<String, Any>): HabitResponse<List<InviteResponse>>
|
||||
suspend fun inviteToGroup(
|
||||
@Path("gid") groupId: String,
|
||||
@Body inviteData: Map<String, Any>,
|
||||
): HabitResponse<List<InviteResponse>>
|
||||
|
||||
@POST("groups/{gid}/reject-invite")
|
||||
suspend fun rejectGroupInvite(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun rejectGroupInvite(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/quests/accept")
|
||||
suspend fun acceptQuest(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun acceptQuest(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/quests/reject")
|
||||
suspend fun rejectQuest(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun rejectQuest(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/quests/cancel")
|
||||
suspend fun cancelQuest(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun cancelQuest(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("groups/{gid}/quests/force-start")
|
||||
suspend fun forceStartQuest(@Path("gid") groupId: String, @Body group: Group): HabitResponse<Quest>
|
||||
suspend fun forceStartQuest(
|
||||
@Path("gid") groupId: String,
|
||||
@Body group: Group,
|
||||
): HabitResponse<Quest>
|
||||
|
||||
@POST("groups/{gid}/quests/invite/{questKey}")
|
||||
suspend fun inviteToQuest(@Path("gid") groupId: String, @Path("questKey") questKey: String): HabitResponse<Quest>
|
||||
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?
|
||||
@Query("includeAllPublicFields") includeAllPublicFields: Boolean?,
|
||||
): HabitResponse<List<Member>>
|
||||
|
||||
@POST("groups/{gid}/quests/abort")
|
||||
suspend fun abortQuest(@Path("gid") groupId: String): HabitResponse<Quest>
|
||||
suspend fun abortQuest(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Quest>
|
||||
|
||||
@POST("groups/{gid}/quests/leave")
|
||||
suspend fun leaveQuest(@Path("gid") groupId: String): HabitResponse<Void>
|
||||
suspend fun leaveQuest(
|
||||
@Path("gid") groupId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("/iap/android/verify")
|
||||
suspend fun validatePurchase(@Body request: PurchaseValidationRequest): HabitResponse<PurchaseValidationResult>
|
||||
suspend fun validatePurchase(
|
||||
@Body request: PurchaseValidationRequest,
|
||||
): HabitResponse<PurchaseValidationResult>
|
||||
|
||||
@POST("/iap/android/subscribe")
|
||||
suspend fun validateSubscription(@Body request: PurchaseValidationRequest): HabitResponse<Void>
|
||||
suspend fun validateSubscription(
|
||||
@Body request: PurchaseValidationRequest,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@GET("/iap/android/subscribe/cancel")
|
||||
suspend fun cancelSubscription(): HabitResponse<Void>
|
||||
|
||||
@POST("/iap/android/norenew-subscribe")
|
||||
suspend fun validateNoRenewSubscription(@Body request: PurchaseValidationRequest): HabitResponse<Void>
|
||||
suspend fun validateNoRenewSubscription(
|
||||
@Body request: PurchaseValidationRequest,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/custom-day-start")
|
||||
suspend fun changeCustomDayStart(@Body updateObject: Map<String, Any>): HabitResponse<Void>
|
||||
suspend fun changeCustomDayStart(
|
||||
@Body updateObject: Map<String, Any>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
// Members URL
|
||||
@GET("members/{mid}")
|
||||
suspend fun getMember(@Path("mid") memberId: String): HabitResponse<Member>
|
||||
suspend fun getMember(
|
||||
@Path("mid") memberId: String,
|
||||
): HabitResponse<Member>
|
||||
|
||||
@GET("members/username/{username}")
|
||||
suspend fun getMemberWithUsername(@Path("username") username: String): HabitResponse<Member>
|
||||
suspend fun getMemberWithUsername(
|
||||
@Path("username") username: String,
|
||||
): HabitResponse<Member>
|
||||
|
||||
@GET("members/{mid}/achievements")
|
||||
suspend fun getMemberAchievements(@Path("mid") memberId: String, @Query("lang") language: String?): HabitResponse<List<Achievement>>
|
||||
suspend fun getMemberAchievements(
|
||||
@Path("mid") memberId: String,
|
||||
@Query("lang") language: String?,
|
||||
): HabitResponse<List<Achievement>>
|
||||
|
||||
@POST("members/send-private-message")
|
||||
suspend fun postPrivateMessage(@Body messageDetails: Map<String, String>): HabitResponse<PostChatMessageResult>
|
||||
suspend fun postPrivateMessage(
|
||||
@Body messageDetails: Map<String, String>,
|
||||
): HabitResponse<PostChatMessageResult>
|
||||
|
||||
@GET("members/find/{username}")
|
||||
suspend fun findUsernames(
|
||||
@Path("username") username: String,
|
||||
@Query("context") context: String?,
|
||||
@Query("id") id: String?
|
||||
@Query("id") id: String?,
|
||||
): HabitResponse<List<FindUsernameResult>>
|
||||
|
||||
@POST("members/flag-private-message/{mid}")
|
||||
suspend fun flagInboxMessage(@Path("mid") mid: String, @Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun flagInboxMessage(
|
||||
@Path("mid") mid: String,
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@GET("shops/{identifier}")
|
||||
suspend fun retrieveShopInventory(@Path("identifier") identifier: String, @Query("lang") language: String?): HabitResponse<Shop>
|
||||
suspend fun retrieveShopInventory(
|
||||
@Path("identifier") identifier: String,
|
||||
@Query("lang") language: String?,
|
||||
): HabitResponse<Shop>
|
||||
|
||||
@GET("shops/market-gear")
|
||||
suspend fun retrieveMarketGear(@Query("lang") language: String?): HabitResponse<Shop>
|
||||
suspend fun retrieveMarketGear(
|
||||
@Query("lang") language: String?,
|
||||
): HabitResponse<Shop>
|
||||
|
||||
// Push notifications
|
||||
@POST("user/push-devices")
|
||||
suspend fun addPushDevice(@Body pushDeviceData: Map<String, String>): HabitResponse<List<Void>>
|
||||
suspend fun addPushDevice(
|
||||
@Body pushDeviceData: Map<String, String>,
|
||||
): HabitResponse<List<Void>>
|
||||
|
||||
@DELETE("user/push-devices/{regId}")
|
||||
suspend fun deletePushDevice(@Path("regId") regId: String): HabitResponse<List<Void>>
|
||||
suspend fun deletePushDevice(
|
||||
@Path("regId") regId: String,
|
||||
): HabitResponse<List<Void>>
|
||||
|
||||
/* challenges api */
|
||||
// challenges api
|
||||
|
||||
@GET("challenges/user")
|
||||
suspend fun getUserChallenges(@Query("page") page: Int?, @Query("member") memberOnly: Boolean): HabitResponse<List<Challenge>>
|
||||
suspend fun getUserChallenges(
|
||||
@Query("page") page: Int?,
|
||||
@Query("member") memberOnly: Boolean,
|
||||
): HabitResponse<List<Challenge>>
|
||||
|
||||
@GET("challenges/user")
|
||||
suspend fun getUserChallenges(@Query("page") page: Int?): HabitResponse<List<Challenge>>
|
||||
suspend fun getUserChallenges(
|
||||
@Query("page") page: Int?,
|
||||
): HabitResponse<List<Challenge>>
|
||||
|
||||
@GET("tasks/challenge/{challengeId}")
|
||||
suspend fun getChallengeTasks(@Path("challengeId") challengeId: String): HabitResponse<TaskList>
|
||||
suspend fun getChallengeTasks(
|
||||
@Path("challengeId") challengeId: String,
|
||||
): HabitResponse<TaskList>
|
||||
|
||||
@GET("challenges/{challengeId}")
|
||||
suspend fun getChallenge(@Path("challengeId") challengeId: String): HabitResponse<Challenge>
|
||||
suspend fun getChallenge(
|
||||
@Path("challengeId") challengeId: String,
|
||||
): HabitResponse<Challenge>
|
||||
|
||||
@POST("challenges/{challengeId}/join")
|
||||
suspend fun joinChallenge(@Path("challengeId") challengeId: String): HabitResponse<Challenge>
|
||||
suspend fun joinChallenge(
|
||||
@Path("challengeId") challengeId: String,
|
||||
): HabitResponse<Challenge>
|
||||
|
||||
@POST("challenges/{challengeId}/leave")
|
||||
suspend fun leaveChallenge(@Path("challengeId") challengeId: String, @Body body: LeaveChallengeBody): HabitResponse<Void>
|
||||
suspend fun leaveChallenge(
|
||||
@Path("challengeId") challengeId: String,
|
||||
@Body body: LeaveChallengeBody,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("challenges")
|
||||
suspend fun createChallenge(@Body challenge: Challenge): HabitResponse<Challenge>
|
||||
suspend fun createChallenge(
|
||||
@Body challenge: Challenge,
|
||||
): HabitResponse<Challenge>
|
||||
|
||||
@POST("tasks/challenge/{challengeId}")
|
||||
suspend fun createChallengeTasks(@Path("challengeId") challengeId: String, @Body tasks: List<Task>): HabitResponse<List<Task>>
|
||||
suspend fun createChallengeTasks(
|
||||
@Path("challengeId") challengeId: String,
|
||||
@Body tasks: List<Task>,
|
||||
): HabitResponse<List<Task>>
|
||||
|
||||
@POST("tasks/challenge/{challengeId}")
|
||||
suspend fun createChallengeTask(@Path("challengeId") challengeId: String, @Body task: Task): HabitResponse<Task>
|
||||
suspend fun createChallengeTask(
|
||||
@Path("challengeId") challengeId: String,
|
||||
@Body task: Task,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@PUT("challenges/{challengeId}")
|
||||
suspend fun updateChallenge(@Path("challengeId") challengeId: String, @Body challenge: Challenge): HabitResponse<Challenge>
|
||||
suspend fun updateChallenge(
|
||||
@Path("challengeId") challengeId: String,
|
||||
@Body challenge: Challenge,
|
||||
): HabitResponse<Challenge>
|
||||
|
||||
@DELETE("challenges/{challengeId}")
|
||||
suspend fun deleteChallenge(@Path("challengeId") challengeId: String): HabitResponse<Void>
|
||||
suspend fun deleteChallenge(
|
||||
@Path("challengeId") challengeId: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
// DEBUG: These calls only work on a local development server
|
||||
|
||||
|
|
@ -403,13 +602,19 @@ interface ApiService {
|
|||
|
||||
// Notifications
|
||||
@POST("notifications/{notificationId}/read")
|
||||
suspend fun readNotification(@Path("notificationId") notificationId: String): HabitResponse<List<Any>>
|
||||
suspend fun readNotification(
|
||||
@Path("notificationId") notificationId: String,
|
||||
): HabitResponse<List<Any>>
|
||||
|
||||
@POST("notifications/read")
|
||||
suspend fun readNotifications(@Body notificationIds: Map<String, List<String>>): HabitResponse<List<Any>>
|
||||
suspend fun readNotifications(
|
||||
@Body notificationIds: Map<String, List<String>>,
|
||||
): HabitResponse<List<Any>>
|
||||
|
||||
@POST("notifications/see")
|
||||
suspend fun seeNotifications(@Body notificationIds: Map<String, List<String>>): HabitResponse<List<Any>>
|
||||
suspend fun seeNotifications(
|
||||
@Body notificationIds: Map<String, List<String>>,
|
||||
): HabitResponse<List<Any>>
|
||||
|
||||
@POST("user/open-mystery-item")
|
||||
suspend fun openMysteryItem(): HabitResponse<Equipment>
|
||||
|
|
@ -418,43 +623,71 @@ interface ApiService {
|
|||
suspend fun runCron(): HabitResponse<Void>
|
||||
|
||||
@POST("user/reset")
|
||||
suspend fun resetAccount(@Body body: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun resetAccount(
|
||||
@Body body: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@HTTP(method = "DELETE", path = "user", hasBody = true)
|
||||
suspend fun deleteAccount(@Body body: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun deleteAccount(
|
||||
@Body body: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@GET("user/toggle-pinned-item/{pinType}/{path}")
|
||||
suspend fun togglePinnedItem(@Path("pinType") pinType: String, @Path("path") path: String): HabitResponse<Void>
|
||||
suspend fun togglePinnedItem(
|
||||
@Path("pinType") pinType: String,
|
||||
@Path("path") path: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/reset-password")
|
||||
suspend fun sendPasswordResetEmail(@Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun sendPasswordResetEmail(
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@PUT("user/auth/update-username")
|
||||
suspend fun updateLoginName(@Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun updateLoginName(
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/auth/verify-username")
|
||||
suspend fun verifyUsername(@Body data: Map<String, String>): HabitResponse<VerifyUsernameResponse>
|
||||
suspend fun verifyUsername(
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<VerifyUsernameResponse>
|
||||
|
||||
@PUT("user/auth/update-email")
|
||||
suspend fun updateEmail(@Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun updateEmail(
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@PUT("user/auth/update-password")
|
||||
suspend fun updatePassword(@Body data: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun updatePassword(
|
||||
@Body data: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/allocate")
|
||||
suspend fun allocatePoint(@Query("stat") stat: String): HabitResponse<Stats>
|
||||
suspend fun allocatePoint(
|
||||
@Query("stat") stat: String,
|
||||
): HabitResponse<Stats>
|
||||
|
||||
@POST("user/allocate-bulk")
|
||||
suspend fun bulkAllocatePoints(@Body stats: Map<String, Map<String, Int>>): HabitResponse<Stats>
|
||||
suspend fun bulkAllocatePoints(
|
||||
@Body stats: Map<String, Map<String, Int>>,
|
||||
): HabitResponse<Stats>
|
||||
|
||||
@POST("members/transfer-gems")
|
||||
suspend fun transferGems(@Body data: Map<String, Any>): HabitResponse<Void>
|
||||
suspend fun transferGems(
|
||||
@Body data: Map<String, Any>,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("tasks/unlink-all/{challengeID}")
|
||||
suspend fun unlinkAllTasks(@Path("challengeID") challengeID: String?, @Query("keep") keepOption: String): HabitResponse<Void>
|
||||
suspend fun unlinkAllTasks(
|
||||
@Path("challengeID") challengeID: String?,
|
||||
@Query("keep") keepOption: String,
|
||||
): HabitResponse<Void>
|
||||
|
||||
@POST("user/block/{userID}")
|
||||
suspend fun blockMember(@Path("userID") userID: String): HabitResponse<List<String>>
|
||||
suspend fun blockMember(
|
||||
@Path("userID") userID: String,
|
||||
): HabitResponse<List<String>>
|
||||
|
||||
@POST("user/reroll")
|
||||
suspend fun reroll(): HabitResponse<User>
|
||||
|
|
@ -465,26 +698,47 @@ interface ApiService {
|
|||
suspend fun getTeamPlans(): HabitResponse<List<TeamPlan>>
|
||||
|
||||
@GET("tasks/group/{groupID}")
|
||||
suspend fun getTeamPlanTasks(@Path("groupID") groupId: String): HabitResponse<TaskList>
|
||||
suspend fun getTeamPlanTasks(
|
||||
@Path("groupID") groupId: String,
|
||||
): HabitResponse<TaskList>
|
||||
|
||||
@POST("tasks/{taskID}/assign")
|
||||
suspend fun assignToTask(@Path("taskID") taskId: String?, @Body ids: List<String>): HabitResponse<Task>
|
||||
suspend fun assignToTask(
|
||||
@Path("taskID") taskId: String?,
|
||||
@Body ids: List<String>,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@POST("tasks/{taskID}/unassign/{userID}")
|
||||
suspend fun unassignFromTask(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse<Task>
|
||||
suspend fun unassignFromTask(
|
||||
@Path("taskID") taskID: String,
|
||||
@Path("userID") userID: String,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@PUT("hall/heroes/{memberID}")
|
||||
suspend fun updateUser(@Path("memberID") memberID: String, @Body updateData: Map<String, Map<String, Boolean>>): HabitResponse<Member>
|
||||
suspend fun updateUser(
|
||||
@Path("memberID") memberID: String,
|
||||
@Body updateData: Map<String, Map<String, Boolean>>,
|
||||
): HabitResponse<Member>
|
||||
|
||||
@GET("hall/heroes/{memberID}")
|
||||
suspend fun getHallMember(@Path("memberID") memberID: String): HabitResponse<Member>
|
||||
suspend fun getHallMember(
|
||||
@Path("memberID") memberID: String,
|
||||
): HabitResponse<Member>
|
||||
|
||||
@POST("tasks/{taskID}/needs-work/{userID}")
|
||||
suspend fun markTaskNeedsWork(@Path("taskID") taskID: String, @Path("userID") userID: String): HabitResponse<Task>
|
||||
suspend fun markTaskNeedsWork(
|
||||
@Path("taskID") taskID: String,
|
||||
@Path("userID") userID: String,
|
||||
): HabitResponse<Task>
|
||||
|
||||
@GET("looking-for-party")
|
||||
suspend fun retrievePartySeekingUsers(@Query("page") page: Int): HabitResponse<List<Member>>
|
||||
suspend fun retrievePartySeekingUsers(
|
||||
@Query("page") page: Int,
|
||||
): HabitResponse<List<Member>>
|
||||
|
||||
@POST("challenges/{challengeId}/flag")
|
||||
suspend fun reportChallenge(@Path("challengeId") challengeid: String, @Body updateData: Map<String, String>): HabitResponse<Void>
|
||||
suspend fun reportChallenge(
|
||||
@Path("challengeId") challengeid: String,
|
||||
@Body updateData: Map<String, String>,
|
||||
): HabitResponse<Void>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,288 +1,499 @@
|
|||
package com.habitrpg.android.habitica.data
|
||||
|
||||
import com.habitrpg.android.habitica.models.Achievement
|
||||
import com.habitrpg.android.habitica.models.ContentResult
|
||||
import com.habitrpg.android.habitica.models.LeaveChallengeBody
|
||||
import com.habitrpg.android.habitica.models.Tag
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.inventory.Equipment
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import com.habitrpg.android.habitica.models.invitations.InviteResponse
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.responses.BulkTaskScoringData
|
||||
import com.habitrpg.android.habitica.models.responses.BuyResponse
|
||||
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult
|
||||
import com.habitrpg.android.habitica.models.responses.SkillResponse
|
||||
import com.habitrpg.android.habitica.models.responses.UnlockResponse
|
||||
import com.habitrpg.android.habitica.models.shops.Shop
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.social.ChatMessage
|
||||
import com.habitrpg.android.habitica.models.social.FindUsernameResult
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.social.InboxConversation
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.tasks.TaskList
|
||||
import com.habitrpg.android.habitica.models.user.Items
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.habitrpg.common.habitica.models.PurchaseValidationRequest
|
||||
import com.habitrpg.common.habitica.models.PurchaseValidationResult
|
||||
import com.habitrpg.common.habitica.models.auth.UserAuthResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.ErrorResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.FeedResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.Status
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirectionData
|
||||
import com.habitrpg.shared.habitica.models.responses.VerifyUsernameResponse
|
||||
import retrofit2.HttpException
|
||||
|
||||
interface ApiClient {
|
||||
|
||||
val hostConfig: HostConfig
|
||||
|
||||
suspend fun getStatus(): Status?
|
||||
|
||||
/* user API */
|
||||
|
||||
suspend fun getTasks(): TaskList?
|
||||
|
||||
/* challenges api */
|
||||
|
||||
suspend fun getUserChallenges(page: Int, memberOnly: Boolean): List<Challenge>?
|
||||
|
||||
suspend fun getWorldState(): WorldState?
|
||||
var languageCode: String?
|
||||
suspend fun getContent(language: String? = null): ContentResult?
|
||||
|
||||
suspend fun updateUser(updateDictionary: Map<String, Any?>): User?
|
||||
|
||||
suspend fun registrationLanguage(registrationLanguage: String): User?
|
||||
|
||||
suspend fun retrieveInAppRewards(): List<ShopItem>?
|
||||
|
||||
suspend fun equipItem(type: String, itemKey: String): Items?
|
||||
|
||||
suspend fun buyItem(itemKey: String, purchaseQuantity: Int): BuyResponse?
|
||||
|
||||
suspend fun purchaseItem(type: String, itemKey: String, purchaseQuantity: Int): Void?
|
||||
|
||||
suspend fun purchaseHourglassItem(type: String, itemKey: String): Void?
|
||||
|
||||
suspend fun purchaseMysterySet(itemKey: String): Void?
|
||||
|
||||
suspend fun purchaseQuest(key: String): Void?
|
||||
suspend fun purchaseSpecialSpell(key: String): Void?
|
||||
suspend fun validateSubscription(request: PurchaseValidationRequest): Any?
|
||||
suspend fun validateNoRenewSubscription(request: PurchaseValidationRequest): Any?
|
||||
suspend fun cancelSubscription(): Void?
|
||||
|
||||
suspend fun sellItem(itemType: String, itemKey: String): User?
|
||||
|
||||
suspend fun feedPet(petKey: String, foodKey: String): FeedResponse?
|
||||
|
||||
suspend fun hatchPet(eggKey: String, hatchingPotionKey: String): Items?
|
||||
suspend fun getTasks(type: String): TaskList?
|
||||
suspend fun getTasks(type: String, dueDate: String): TaskList?
|
||||
|
||||
suspend fun unlockPath(path: String): UnlockResponse?
|
||||
|
||||
suspend fun getTask(id: String): Task?
|
||||
|
||||
suspend fun postTaskDirection(id: String, direction: String): TaskDirectionData?
|
||||
suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData?
|
||||
|
||||
suspend fun postTaskNewPosition(id: String, position: Int): List<String>?
|
||||
|
||||
suspend fun scoreChecklistItem(taskId: String, itemId: String): Task?
|
||||
|
||||
suspend fun createTask(item: Task): Task?
|
||||
suspend fun createGroupTask(groupId: String, item: Task): Task?
|
||||
|
||||
suspend fun createTasks(tasks: List<Task>): List<Task>?
|
||||
|
||||
suspend fun updateTask(id: String, item: Task): Task?
|
||||
|
||||
suspend fun deleteTask(id: String): Void?
|
||||
|
||||
suspend fun createTag(tag: Tag): Tag?
|
||||
|
||||
suspend fun updateTag(id: String, tag: Tag): Tag?
|
||||
|
||||
suspend fun deleteTag(id: String): Void?
|
||||
|
||||
suspend fun registerUser(username: String, email: String, password: String, confirmPassword: String): UserAuthResponse?
|
||||
|
||||
suspend fun connectUser(username: String, password: String): UserAuthResponse?
|
||||
|
||||
suspend fun connectSocial(network: String, userId: String, accessToken: String): UserAuthResponse?
|
||||
suspend fun disconnectSocial(network: String): Void?
|
||||
|
||||
suspend fun loginApple(authToken: String): UserAuthResponse?
|
||||
|
||||
suspend fun sleep(): Boolean?
|
||||
suspend fun revive(): Items?
|
||||
|
||||
suspend fun useSkill(skillName: String, targetType: String, targetId: String): SkillResponse?
|
||||
|
||||
suspend fun useSkill(skillName: String, targetType: String): SkillResponse?
|
||||
|
||||
suspend fun changeClass(className: String?): User?
|
||||
|
||||
suspend fun disableClasses(): User?
|
||||
|
||||
suspend fun markPrivateMessagesRead()
|
||||
|
||||
/* Group API */
|
||||
|
||||
suspend fun listGroups(type: String): List<Group>?
|
||||
|
||||
suspend fun getGroup(groupId: String): Group?
|
||||
|
||||
suspend fun createGroup(group: Group): Group?
|
||||
suspend fun updateGroup(id: String, item: Group): Group?
|
||||
suspend fun removeMemberFromGroup(groupID: String, userID: String): Void?
|
||||
|
||||
suspend fun listGroupChat(groupId: String): List<ChatMessage>?
|
||||
|
||||
suspend fun joinGroup(groupId: String): Group?
|
||||
|
||||
suspend fun leaveGroup(groupId: String, keepChallenges: String): Void?
|
||||
|
||||
suspend fun postGroupChat(groupId: String, message: Map<String, String>): PostChatMessageResult?
|
||||
|
||||
suspend fun deleteMessage(groupId: String, messageId: String): Void?
|
||||
suspend fun deleteInboxMessage(id: String): Void?
|
||||
|
||||
suspend fun getGroupMembers(groupId: String, includeAllPublicFields: Boolean?): List<Member>?
|
||||
|
||||
suspend fun getGroupMembers(groupId: String, includeAllPublicFields: Boolean?, lastId: String): List<Member>?
|
||||
|
||||
// Like returns the full chat list
|
||||
suspend fun likeMessage(groupId: String, mid: String): ChatMessage?
|
||||
|
||||
suspend fun flagMessage(groupId: String, mid: String, data: MutableMap<String, String>): Void?
|
||||
suspend fun flagInboxMessage(mid: String, data: MutableMap<String, String>): Void?
|
||||
|
||||
suspend fun reportMember(mid: String, data: Map<String, String>): Void?
|
||||
|
||||
suspend fun seenMessages(groupId: String): Void?
|
||||
|
||||
suspend fun inviteToGroup(groupId: String, inviteData: Map<String, Any>): List<InviteResponse>?
|
||||
|
||||
suspend fun rejectGroupInvite(groupId: String): Void?
|
||||
|
||||
suspend fun acceptQuest(groupId: String): Void?
|
||||
|
||||
suspend fun rejectQuest(groupId: String): Void?
|
||||
|
||||
suspend fun cancelQuest(groupId: String): Void?
|
||||
|
||||
suspend fun forceStartQuest(groupId: String, group: Group): Quest?
|
||||
|
||||
suspend fun inviteToQuest(groupId: String, questKey: String): Quest?
|
||||
|
||||
suspend fun abortQuest(groupId: String): Quest?
|
||||
|
||||
suspend fun leaveQuest(groupId: String): Void?
|
||||
|
||||
suspend fun validatePurchase(request: PurchaseValidationRequest): PurchaseValidationResult?
|
||||
|
||||
suspend fun changeCustomDayStart(updateObject: Map<String, Any>): Void?
|
||||
|
||||
// Members URL
|
||||
suspend fun getMember(memberId: String): Member?
|
||||
suspend fun getMemberWithUsername(username: String): Member?
|
||||
|
||||
suspend fun getMemberAchievements(memberId: String): List<Achievement>?
|
||||
|
||||
suspend fun postPrivateMessage(messageDetails: Map<String, String>): PostChatMessageResult?
|
||||
|
||||
suspend fun retrieveShopIventory(identifier: String): Shop?
|
||||
|
||||
// Push notifications
|
||||
suspend fun addPushDevice(pushDeviceData: Map<String, String>): List<Void>?
|
||||
|
||||
suspend fun deletePushDevice(regId: String): List<Void>?
|
||||
|
||||
suspend fun getChallengeTasks(challengeId: String): TaskList?
|
||||
|
||||
suspend fun getChallenge(challengeId: String): Challenge?
|
||||
|
||||
suspend fun joinChallenge(challengeId: String): Challenge?
|
||||
|
||||
suspend fun leaveChallenge(challengeId: String, body: LeaveChallengeBody): Void?
|
||||
|
||||
suspend fun createChallenge(challenge: Challenge): Challenge?
|
||||
|
||||
suspend fun createChallengeTasks(challengeId: String, tasks: List<Task>): List<Task>?
|
||||
suspend fun createChallengeTask(challengeId: String, task: Task): Task?
|
||||
suspend fun updateChallenge(challenge: Challenge): Challenge?
|
||||
suspend fun deleteChallenge(challengeId: String): Void?
|
||||
|
||||
// DEBUG: These calls only work on a local development server
|
||||
|
||||
suspend fun debugAddTenGems(): Void?
|
||||
|
||||
suspend fun getNews(): List<Any>?
|
||||
|
||||
// Notifications
|
||||
suspend fun readNotification(notificationId: String): List<Any>?
|
||||
suspend fun readNotifications(notificationIds: Map<String, List<String>>): List<Any>?
|
||||
suspend fun seeNotifications(notificationIds: Map<String, List<String>>): List<Any>?
|
||||
|
||||
fun getErrorResponse(throwable: HttpException): ErrorResponse
|
||||
|
||||
fun updateAuthenticationCredentials(userID: String?, apiToken: String?)
|
||||
|
||||
fun hasAuthenticationKeys(): Boolean
|
||||
|
||||
suspend fun retrieveUser(withTasks: Boolean = false): User?
|
||||
suspend fun retrieveInboxMessages(uuid: String, page: Int): List<ChatMessage>?
|
||||
suspend fun retrieveInboxConversations(): List<InboxConversation>?
|
||||
|
||||
suspend fun openMysteryItem(): Equipment?
|
||||
|
||||
suspend fun runCron(): Void?
|
||||
|
||||
suspend fun reroll(): User?
|
||||
|
||||
suspend fun resetAccount(password: String): Void?
|
||||
suspend fun deleteAccount(password: String): Void?
|
||||
|
||||
suspend fun togglePinnedItem(pinType: String, path: String): Void?
|
||||
|
||||
suspend fun sendPasswordResetEmail(email: String): Void?
|
||||
|
||||
suspend fun updateLoginName(newLoginName: String, password: String): Void?
|
||||
suspend fun updateUsername(newLoginName: String): Void?
|
||||
|
||||
suspend fun updateEmail(newEmail: String, password: String): Void?
|
||||
|
||||
suspend fun updatePassword(oldPassword: String, newPassword: String, newPasswordConfirmation: String): Void?
|
||||
|
||||
suspend fun allocatePoint(stat: String): Stats?
|
||||
|
||||
suspend fun bulkAllocatePoints(strength: Int, intelligence: Int, constitution: Int, perception: Int): Stats?
|
||||
|
||||
suspend fun retrieveMarketGear(): Shop?
|
||||
suspend fun verifyUsername(username: String): VerifyUsernameResponse?
|
||||
fun updateServerUrl(newAddress: String?)
|
||||
suspend fun findUsernames(username: String, context: String?, id: String?): List<FindUsernameResult>?
|
||||
|
||||
suspend fun transferGems(giftedID: String, amount: Int): Void?
|
||||
suspend fun unlinkAllTasks(challengeID: String?, keepOption: String): Void?
|
||||
suspend fun blockMember(userID: String): List<String>?
|
||||
suspend fun getTeamPlans(): List<TeamPlan>?
|
||||
suspend fun getTeamPlanTasks(teamID: String): TaskList?
|
||||
suspend fun assignToTask(taskId: String, ids: List<String>): Task?
|
||||
suspend fun unassignFromTask(taskId: String, userID: String): Task?
|
||||
suspend fun updateMember(memberID: String, updateData: Map<String, Map<String, Boolean>>): Member?
|
||||
suspend fun getHallMember(userId: String): Member?
|
||||
suspend fun markTaskNeedsWork(taskID: String, userID: String): Task?
|
||||
suspend fun retrievePartySeekingUsers(page: Int): List<Member>?
|
||||
suspend fun getGroupInvites(groupId: String, includeAllPublicFields: Boolean?): List<Member>?
|
||||
suspend fun syncUserStats(): User?
|
||||
suspend fun reportChallenge(challengeid: String, updateData: Map<String, String>): Void?
|
||||
}
|
||||
package com.habitrpg.android.habitica.data
|
||||
|
||||
import com.habitrpg.android.habitica.models.Achievement
|
||||
import com.habitrpg.android.habitica.models.ContentResult
|
||||
import com.habitrpg.android.habitica.models.LeaveChallengeBody
|
||||
import com.habitrpg.android.habitica.models.Tag
|
||||
import com.habitrpg.android.habitica.models.TeamPlan
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.inventory.Equipment
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import com.habitrpg.android.habitica.models.invitations.InviteResponse
|
||||
import com.habitrpg.android.habitica.models.members.Member
|
||||
import com.habitrpg.android.habitica.models.responses.BulkTaskScoringData
|
||||
import com.habitrpg.android.habitica.models.responses.BuyResponse
|
||||
import com.habitrpg.android.habitica.models.responses.PostChatMessageResult
|
||||
import com.habitrpg.android.habitica.models.responses.SkillResponse
|
||||
import com.habitrpg.android.habitica.models.responses.UnlockResponse
|
||||
import com.habitrpg.android.habitica.models.shops.Shop
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.social.Challenge
|
||||
import com.habitrpg.android.habitica.models.social.ChatMessage
|
||||
import com.habitrpg.android.habitica.models.social.FindUsernameResult
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.models.social.InboxConversation
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.tasks.TaskList
|
||||
import com.habitrpg.android.habitica.models.user.Items
|
||||
import com.habitrpg.android.habitica.models.user.Stats
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.common.habitica.api.HostConfig
|
||||
import com.habitrpg.common.habitica.models.PurchaseValidationRequest
|
||||
import com.habitrpg.common.habitica.models.PurchaseValidationResult
|
||||
import com.habitrpg.common.habitica.models.auth.UserAuthResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.ErrorResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.FeedResponse
|
||||
import com.habitrpg.shared.habitica.models.responses.Status
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirectionData
|
||||
import com.habitrpg.shared.habitica.models.responses.VerifyUsernameResponse
|
||||
import retrofit2.HttpException
|
||||
|
||||
interface ApiClient {
|
||||
val hostConfig: HostConfig
|
||||
|
||||
suspend fun getStatus(): Status?
|
||||
|
||||
// user API
|
||||
|
||||
suspend fun getTasks(): TaskList?
|
||||
|
||||
// challenges api
|
||||
|
||||
suspend fun getUserChallenges(
|
||||
page: Int,
|
||||
memberOnly: Boolean,
|
||||
): List<Challenge>?
|
||||
|
||||
suspend fun getWorldState(): WorldState?
|
||||
|
||||
var languageCode: String?
|
||||
|
||||
suspend fun getContent(language: String? = null): ContentResult?
|
||||
|
||||
suspend fun updateUser(updateDictionary: Map<String, Any?>): User?
|
||||
|
||||
suspend fun registrationLanguage(registrationLanguage: String): User?
|
||||
|
||||
suspend fun retrieveInAppRewards(): List<ShopItem>?
|
||||
|
||||
suspend fun equipItem(
|
||||
type: String,
|
||||
itemKey: String,
|
||||
): Items?
|
||||
|
||||
suspend fun buyItem(
|
||||
itemKey: String,
|
||||
purchaseQuantity: Int,
|
||||
): BuyResponse?
|
||||
|
||||
suspend fun purchaseItem(
|
||||
type: String,
|
||||
itemKey: String,
|
||||
purchaseQuantity: Int,
|
||||
): Void?
|
||||
|
||||
suspend fun purchaseHourglassItem(
|
||||
type: String,
|
||||
itemKey: String,
|
||||
): Void?
|
||||
|
||||
suspend fun purchaseMysterySet(itemKey: String): Void?
|
||||
|
||||
suspend fun purchaseQuest(key: String): Void?
|
||||
|
||||
suspend fun purchaseSpecialSpell(key: String): Void?
|
||||
|
||||
suspend fun validateSubscription(request: PurchaseValidationRequest): Any?
|
||||
|
||||
suspend fun validateNoRenewSubscription(request: PurchaseValidationRequest): Any?
|
||||
|
||||
suspend fun cancelSubscription(): Void?
|
||||
|
||||
suspend fun sellItem(
|
||||
itemType: String,
|
||||
itemKey: String,
|
||||
): User?
|
||||
|
||||
suspend fun feedPet(
|
||||
petKey: String,
|
||||
foodKey: String,
|
||||
): FeedResponse?
|
||||
|
||||
suspend fun hatchPet(
|
||||
eggKey: String,
|
||||
hatchingPotionKey: String,
|
||||
): Items?
|
||||
|
||||
suspend fun getTasks(type: String): TaskList?
|
||||
|
||||
suspend fun getTasks(
|
||||
type: String,
|
||||
dueDate: String,
|
||||
): TaskList?
|
||||
|
||||
suspend fun unlockPath(path: String): UnlockResponse?
|
||||
|
||||
suspend fun getTask(id: String): Task?
|
||||
|
||||
suspend fun postTaskDirection(
|
||||
id: String,
|
||||
direction: String,
|
||||
): TaskDirectionData?
|
||||
|
||||
suspend fun bulkScoreTasks(data: List<Map<String, String>>): BulkTaskScoringData?
|
||||
|
||||
suspend fun postTaskNewPosition(
|
||||
id: String,
|
||||
position: Int,
|
||||
): List<String>?
|
||||
|
||||
suspend fun scoreChecklistItem(
|
||||
taskId: String,
|
||||
itemId: String,
|
||||
): Task?
|
||||
|
||||
suspend fun createTask(item: Task): Task?
|
||||
|
||||
suspend fun createGroupTask(
|
||||
groupId: String,
|
||||
item: Task,
|
||||
): Task?
|
||||
|
||||
suspend fun createTasks(tasks: List<Task>): List<Task>?
|
||||
|
||||
suspend fun updateTask(
|
||||
id: String,
|
||||
item: Task,
|
||||
): Task?
|
||||
|
||||
suspend fun deleteTask(id: String): Void?
|
||||
|
||||
suspend fun createTag(tag: Tag): Tag?
|
||||
|
||||
suspend fun updateTag(
|
||||
id: String,
|
||||
tag: Tag,
|
||||
): Tag?
|
||||
|
||||
suspend fun deleteTag(id: String): Void?
|
||||
|
||||
suspend fun registerUser(
|
||||
username: String,
|
||||
email: String,
|
||||
password: String,
|
||||
confirmPassword: String,
|
||||
): UserAuthResponse?
|
||||
|
||||
suspend fun connectUser(
|
||||
username: String,
|
||||
password: String,
|
||||
): UserAuthResponse?
|
||||
|
||||
suspend fun connectSocial(
|
||||
network: String,
|
||||
userId: String,
|
||||
accessToken: String,
|
||||
): UserAuthResponse?
|
||||
|
||||
suspend fun disconnectSocial(network: String): Void?
|
||||
|
||||
suspend fun loginApple(authToken: String): UserAuthResponse?
|
||||
|
||||
suspend fun sleep(): Boolean?
|
||||
|
||||
suspend fun revive(): Items?
|
||||
|
||||
suspend fun useSkill(
|
||||
skillName: String,
|
||||
targetType: String,
|
||||
targetId: String,
|
||||
): SkillResponse?
|
||||
|
||||
suspend fun useSkill(
|
||||
skillName: String,
|
||||
targetType: String,
|
||||
): SkillResponse?
|
||||
|
||||
suspend fun changeClass(className: String?): User?
|
||||
|
||||
suspend fun disableClasses(): User?
|
||||
|
||||
suspend fun markPrivateMessagesRead()
|
||||
|
||||
// Group API
|
||||
|
||||
suspend fun listGroups(type: String): List<Group>?
|
||||
|
||||
suspend fun getGroup(groupId: String): Group?
|
||||
|
||||
suspend fun createGroup(group: Group): Group?
|
||||
|
||||
suspend fun updateGroup(
|
||||
id: String,
|
||||
item: Group,
|
||||
): Group?
|
||||
|
||||
suspend fun removeMemberFromGroup(
|
||||
groupID: String,
|
||||
userID: String,
|
||||
): Void?
|
||||
|
||||
suspend fun listGroupChat(groupId: String): List<ChatMessage>?
|
||||
|
||||
suspend fun joinGroup(groupId: String): Group?
|
||||
|
||||
suspend fun leaveGroup(
|
||||
groupId: String,
|
||||
keepChallenges: String,
|
||||
): Void?
|
||||
|
||||
suspend fun postGroupChat(
|
||||
groupId: String,
|
||||
message: Map<String, String>,
|
||||
): PostChatMessageResult?
|
||||
|
||||
suspend fun deleteMessage(
|
||||
groupId: String,
|
||||
messageId: String,
|
||||
): Void?
|
||||
|
||||
suspend fun deleteInboxMessage(id: String): Void?
|
||||
|
||||
suspend fun getGroupMembers(
|
||||
groupId: String,
|
||||
includeAllPublicFields: Boolean?,
|
||||
): List<Member>?
|
||||
|
||||
suspend fun getGroupMembers(
|
||||
groupId: String,
|
||||
includeAllPublicFields: Boolean?,
|
||||
lastId: String,
|
||||
): List<Member>?
|
||||
|
||||
// Like returns the full chat list
|
||||
suspend fun likeMessage(
|
||||
groupId: String,
|
||||
mid: String,
|
||||
): ChatMessage?
|
||||
|
||||
suspend fun flagMessage(
|
||||
groupId: String,
|
||||
mid: String,
|
||||
data: MutableMap<String, String>,
|
||||
): Void?
|
||||
|
||||
suspend fun flagInboxMessage(
|
||||
mid: String,
|
||||
data: MutableMap<String, String>,
|
||||
): Void?
|
||||
|
||||
suspend fun reportMember(
|
||||
mid: String,
|
||||
data: Map<String, String>,
|
||||
): Void?
|
||||
|
||||
suspend fun seenMessages(groupId: String): Void?
|
||||
|
||||
suspend fun inviteToGroup(
|
||||
groupId: String,
|
||||
inviteData: Map<String, Any>,
|
||||
): List<InviteResponse>?
|
||||
|
||||
suspend fun rejectGroupInvite(groupId: String): Void?
|
||||
|
||||
suspend fun acceptQuest(groupId: String): Void?
|
||||
|
||||
suspend fun rejectQuest(groupId: String): Void?
|
||||
|
||||
suspend fun cancelQuest(groupId: String): Void?
|
||||
|
||||
suspend fun forceStartQuest(
|
||||
groupId: String,
|
||||
group: Group,
|
||||
): Quest?
|
||||
|
||||
suspend fun inviteToQuest(
|
||||
groupId: String,
|
||||
questKey: String,
|
||||
): Quest?
|
||||
|
||||
suspend fun abortQuest(groupId: String): Quest?
|
||||
|
||||
suspend fun leaveQuest(groupId: String): Void?
|
||||
|
||||
suspend fun validatePurchase(request: PurchaseValidationRequest): PurchaseValidationResult?
|
||||
|
||||
suspend fun changeCustomDayStart(updateObject: Map<String, Any>): Void?
|
||||
|
||||
// Members URL
|
||||
suspend fun getMember(memberId: String): Member?
|
||||
|
||||
suspend fun getMemberWithUsername(username: String): Member?
|
||||
|
||||
suspend fun getMemberAchievements(memberId: String): List<Achievement>?
|
||||
|
||||
suspend fun postPrivateMessage(messageDetails: Map<String, String>): PostChatMessageResult?
|
||||
|
||||
suspend fun retrieveShopIventory(identifier: String): Shop?
|
||||
|
||||
// Push notifications
|
||||
suspend fun addPushDevice(pushDeviceData: Map<String, String>): List<Void>?
|
||||
|
||||
suspend fun deletePushDevice(regId: String): List<Void>?
|
||||
|
||||
suspend fun getChallengeTasks(challengeId: String): TaskList?
|
||||
|
||||
suspend fun getChallenge(challengeId: String): Challenge?
|
||||
|
||||
suspend fun joinChallenge(challengeId: String): Challenge?
|
||||
|
||||
suspend fun leaveChallenge(
|
||||
challengeId: String,
|
||||
body: LeaveChallengeBody,
|
||||
): Void?
|
||||
|
||||
suspend fun createChallenge(challenge: Challenge): Challenge?
|
||||
|
||||
suspend fun createChallengeTasks(
|
||||
challengeId: String,
|
||||
tasks: List<Task>,
|
||||
): List<Task>?
|
||||
|
||||
suspend fun createChallengeTask(
|
||||
challengeId: String,
|
||||
task: Task,
|
||||
): Task?
|
||||
|
||||
suspend fun updateChallenge(challenge: Challenge): Challenge?
|
||||
|
||||
suspend fun deleteChallenge(challengeId: String): Void?
|
||||
|
||||
// DEBUG: These calls only work on a local development server
|
||||
|
||||
suspend fun debugAddTenGems(): Void?
|
||||
|
||||
suspend fun getNews(): List<Any>?
|
||||
|
||||
// Notifications
|
||||
suspend fun readNotification(notificationId: String): List<Any>?
|
||||
|
||||
suspend fun readNotifications(notificationIds: Map<String, List<String>>): List<Any>?
|
||||
|
||||
suspend fun seeNotifications(notificationIds: Map<String, List<String>>): List<Any>?
|
||||
|
||||
fun getErrorResponse(throwable: HttpException): ErrorResponse
|
||||
|
||||
fun updateAuthenticationCredentials(
|
||||
userID: String?,
|
||||
apiToken: String?,
|
||||
)
|
||||
|
||||
fun hasAuthenticationKeys(): Boolean
|
||||
|
||||
suspend fun retrieveUser(withTasks: Boolean = false): User?
|
||||
|
||||
suspend fun retrieveInboxMessages(
|
||||
uuid: String,
|
||||
page: Int,
|
||||
): List<ChatMessage>?
|
||||
|
||||
suspend fun retrieveInboxConversations(): List<InboxConversation>?
|
||||
|
||||
suspend fun openMysteryItem(): Equipment?
|
||||
|
||||
suspend fun runCron(): Void?
|
||||
|
||||
suspend fun reroll(): User?
|
||||
|
||||
suspend fun resetAccount(password: String): Void?
|
||||
|
||||
suspend fun deleteAccount(password: String): Void?
|
||||
|
||||
suspend fun togglePinnedItem(
|
||||
pinType: String,
|
||||
path: String,
|
||||
): Void?
|
||||
|
||||
suspend fun sendPasswordResetEmail(email: String): Void?
|
||||
|
||||
suspend fun updateLoginName(
|
||||
newLoginName: String,
|
||||
password: String,
|
||||
): Void?
|
||||
|
||||
suspend fun updateUsername(newLoginName: String): Void?
|
||||
|
||||
suspend fun updateEmail(
|
||||
newEmail: String,
|
||||
password: String,
|
||||
): Void?
|
||||
|
||||
suspend fun updatePassword(
|
||||
oldPassword: String,
|
||||
newPassword: String,
|
||||
newPasswordConfirmation: String,
|
||||
): Void?
|
||||
|
||||
suspend fun allocatePoint(stat: String): Stats?
|
||||
|
||||
suspend fun bulkAllocatePoints(
|
||||
strength: Int,
|
||||
intelligence: Int,
|
||||
constitution: Int,
|
||||
perception: Int,
|
||||
): Stats?
|
||||
|
||||
suspend fun retrieveMarketGear(): Shop?
|
||||
|
||||
suspend fun verifyUsername(username: String): VerifyUsernameResponse?
|
||||
|
||||
fun updateServerUrl(newAddress: String?)
|
||||
|
||||
suspend fun findUsernames(
|
||||
username: String,
|
||||
context: String?,
|
||||
id: String?,
|
||||
): List<FindUsernameResult>?
|
||||
|
||||
suspend fun transferGems(
|
||||
giftedID: String,
|
||||
amount: Int,
|
||||
): Void?
|
||||
|
||||
suspend fun unlinkAllTasks(
|
||||
challengeID: String?,
|
||||
keepOption: String,
|
||||
): Void?
|
||||
|
||||
suspend fun blockMember(userID: String): List<String>?
|
||||
|
||||
suspend fun getTeamPlans(): List<TeamPlan>?
|
||||
|
||||
suspend fun getTeamPlanTasks(teamID: String): TaskList?
|
||||
|
||||
suspend fun assignToTask(
|
||||
taskId: String,
|
||||
ids: List<String>,
|
||||
): Task?
|
||||
|
||||
suspend fun unassignFromTask(
|
||||
taskId: String,
|
||||
userID: String,
|
||||
): Task?
|
||||
|
||||
suspend fun updateMember(
|
||||
memberID: String,
|
||||
updateData: Map<String, Map<String, Boolean>>,
|
||||
): Member?
|
||||
|
||||
suspend fun getHallMember(userId: String): Member?
|
||||
|
||||
suspend fun markTaskNeedsWork(
|
||||
taskID: String,
|
||||
userID: String,
|
||||
): Task?
|
||||
|
||||
suspend fun retrievePartySeekingUsers(page: Int): List<Member>?
|
||||
|
||||
suspend fun getGroupInvites(
|
||||
groupId: String,
|
||||
includeAllPublicFields: Boolean?,
|
||||
): List<Member>?
|
||||
|
||||
suspend fun syncUserStats(): User?
|
||||
|
||||
suspend fun reportChallenge(
|
||||
challengeid: String,
|
||||
updateData: Map<String, String>,
|
||||
): Void?
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,33 +1,34 @@
|
|||
package com.habitrpg.android.habitica.models.shops
|
||||
|
||||
import android.content.Context
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
||||
class Shop {
|
||||
var identifier: String = ""
|
||||
var text: String = ""
|
||||
var notes: String = ""
|
||||
var imageName: String = ""
|
||||
|
||||
var categories: MutableList<ShopCategory> = ArrayList()
|
||||
|
||||
val npcNameResource: Int
|
||||
get() = when (identifier) {
|
||||
MARKET -> R.string.market_owner
|
||||
QUEST_SHOP -> R.string.questShop_owner
|
||||
SEASONAL_SHOP -> R.string.seasonalShop_owner
|
||||
TIME_TRAVELERS_SHOP -> R.string.timetravelers_owner
|
||||
CUSTOMIZATIONS -> R.string.customizations_owner
|
||||
else -> R.string.market_owner
|
||||
}
|
||||
|
||||
fun getNpcName(context: Context): String = context.getString(npcNameResource)
|
||||
|
||||
companion object {
|
||||
const val MARKET = "market"
|
||||
const val QUEST_SHOP = "questShop"
|
||||
const val TIME_TRAVELERS_SHOP = "timeTravelersShop"
|
||||
const val SEASONAL_SHOP = "seasonalShop"
|
||||
const val CUSTOMIZATIONS = "customizations"
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.models.shops
|
||||
|
||||
import android.content.Context
|
||||
import com.habitrpg.android.habitica.R
|
||||
|
||||
class Shop {
|
||||
var identifier: String = ""
|
||||
var text: String = ""
|
||||
var notes: String = ""
|
||||
var imageName: String = ""
|
||||
|
||||
var categories: MutableList<ShopCategory> = ArrayList()
|
||||
|
||||
val npcNameResource: Int
|
||||
get() =
|
||||
when (identifier) {
|
||||
MARKET -> R.string.market_owner
|
||||
QUEST_SHOP -> R.string.questShop_owner
|
||||
SEASONAL_SHOP -> R.string.seasonalShop_owner
|
||||
TIME_TRAVELERS_SHOP -> R.string.timetravelers_owner
|
||||
CUSTOMIZATIONS -> R.string.customizations_owner
|
||||
else -> R.string.market_owner
|
||||
}
|
||||
|
||||
fun getNpcName(context: Context): String = context.getString(npcNameResource)
|
||||
|
||||
companion object {
|
||||
const val MARKET = "market"
|
||||
const val QUEST_SHOP = "questShop"
|
||||
const val TIME_TRAVELERS_SHOP = "timeTravelersShop"
|
||||
const val SEASONAL_SHOP = "seasonalShop"
|
||||
const val CUSTOMIZATIONS = "customizations"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,225 +1,232 @@
|
|||
package com.habitrpg.android.habitica.models.shops
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.BaseObject
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.inventory.ItemEvent
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import java.util.Date
|
||||
|
||||
open class ShopItem : RealmObject(), BaseObject {
|
||||
@PrimaryKey
|
||||
var key: String = ""
|
||||
var text: String? = ""
|
||||
var notes: String? = ""
|
||||
|
||||
@SerializedName("class")
|
||||
var imageName: String? = null
|
||||
get() {
|
||||
return if (field != null) {
|
||||
if (field!!.contains(" ")) {
|
||||
field!!.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
|
||||
} else {
|
||||
field
|
||||
}
|
||||
} else {
|
||||
"shop_$key"
|
||||
}
|
||||
}
|
||||
|
||||
var value: Int = 0
|
||||
var locked: Boolean = false
|
||||
var isLimited: Boolean = false
|
||||
var currency: String? = null
|
||||
var purchaseType: String = ""
|
||||
var categoryIdentifier: String = ""
|
||||
var limitedNumberLeft: Int? = null
|
||||
var unlockCondition: ShopItemUnlockCondition? = null
|
||||
var path: String? = null
|
||||
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
|
||||
var endDate: Date? = null
|
||||
|
||||
val availableUntil: Date?
|
||||
get() {
|
||||
return endDate ?: event?.end
|
||||
}
|
||||
|
||||
var setImageNames = RealmList<String>()
|
||||
|
||||
val isTypeItem: Boolean
|
||||
get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType || "debuffPotion" == purchaseType || "fortify" == purchaseType
|
||||
|
||||
val isTypeQuest: Boolean
|
||||
get() = "quests" == purchaseType
|
||||
|
||||
val isTypeGear: Boolean
|
||||
get() = "gear" == purchaseType
|
||||
|
||||
val isTypeAnimal: Boolean
|
||||
get() = "pets" == purchaseType || "mounts" == purchaseType
|
||||
|
||||
val canPurchaseBulk: Boolean
|
||||
get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "gems" == purchaseType
|
||||
|
||||
fun canAfford(user: User?, quantity: Int): Boolean = when (currency) {
|
||||
"gold" -> (value * quantity) <= (user?.stats?.gp ?: 0.0)
|
||||
"gems" -> (value * quantity) <= (user?.gemCount ?: 0)
|
||||
"hourglasses" -> (value * quantity) <= (user?.hourglassCount ?: 0)
|
||||
else -> true
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other != null && ShopItem::class.java.isAssignableFrom(other.javaClass)) {
|
||||
val otherItem = other as? ShopItem
|
||||
return this.key == otherItem?.key
|
||||
}
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return this.key.hashCode()
|
||||
}
|
||||
|
||||
fun shortLockedReason(context: Context): String? {
|
||||
return when {
|
||||
unlockCondition != null -> {
|
||||
unlockCondition?.shortReadableUnlockCondition(context)
|
||||
}
|
||||
previous != null -> {
|
||||
try {
|
||||
val thisNumber = Character.getNumericValue(key.last())
|
||||
context.getString(R.string.unlock_previous_short, thisNumber - 1)
|
||||
} catch (e: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
level != null -> {
|
||||
context.getString(R.string.level_unabbreviated, level ?: 0)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun lockedReason(context: Context): String? {
|
||||
return when {
|
||||
unlockCondition != null -> {
|
||||
unlockCondition?.readableUnlockCondition(context)
|
||||
}
|
||||
previous != null -> {
|
||||
try {
|
||||
val thisNumber = Character.getNumericValue(key.last())
|
||||
context.getString(R.string.unlock_previous, thisNumber - 1)
|
||||
} catch (e: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
level != null -> {
|
||||
context.getString(R.string.unlock_level, level ?: 0)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val GEM_FOR_GOLD = "gem"
|
||||
|
||||
fun makeGemItem(res: Resources?): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = GEM_FOR_GOLD
|
||||
item.text = res?.getString(R.string.gem_shop) ?: ""
|
||||
item.notes = res?.getString(R.string.gem_for_gold_description) ?: ""
|
||||
item.imageName = "gem_shop"
|
||||
item.value = 20
|
||||
item.currency = "gold"
|
||||
item.purchaseType = "gems"
|
||||
item.pinType = "gem"
|
||||
item.path = "special.gems"
|
||||
return item
|
||||
}
|
||||
|
||||
fun makeFortifyItem(res: Resources?): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = "fortify"
|
||||
item.text = res?.getString(R.string.fortify_shop) ?: ""
|
||||
item.notes = res?.getString(R.string.fortify_shop_description) ?: ""
|
||||
item.imageName = "inventory_special_fortify"
|
||||
item.value = 4
|
||||
item.currency = "gems"
|
||||
item.pinType = "fortify"
|
||||
item.path = "special.fortify"
|
||||
item.purchaseType = "fortify"
|
||||
return item
|
||||
}
|
||||
|
||||
fun fromCustomization(customization: Customization, userSize: String?, hairColor: String?): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = customization.identifier ?: ""
|
||||
item.text = customization.text
|
||||
item.currency = "gems"
|
||||
item.notes = customization.notes
|
||||
item.value = customization.price ?: 0
|
||||
item.path = customization.path
|
||||
item.unlockPath = customization.unlockPath
|
||||
item.pinType = customization.type
|
||||
if (customization.type == "background") {
|
||||
item.purchaseType = "background"
|
||||
item.imageName = customization.getImageName(userSize, hairColor)
|
||||
} else {
|
||||
item.purchaseType = "customization"
|
||||
item.imageName = customization.getIconName(userSize, hairColor)
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
fun fromCustomizationSet(
|
||||
set: CustomizationSet,
|
||||
additionalSetItems: List<Customization>?,
|
||||
userSize: String?,
|
||||
hairColor: String?
|
||||
): ShopItem {
|
||||
val item = ShopItem()
|
||||
var path = ""
|
||||
for (customization in set.customizations) {
|
||||
path = path + "," + customization.unlockPath
|
||||
item.setImageNames.add(customization.getIconName(userSize, hairColor))
|
||||
}
|
||||
for (customization in additionalSetItems ?: emptyList()) {
|
||||
path = path + "," + customization.unlockPath
|
||||
item.setImageNames.add(customization.getIconName(userSize, hairColor))
|
||||
}
|
||||
if (path.isEmpty()) {
|
||||
item.unlockPath = path
|
||||
} else {
|
||||
item.unlockPath = path.substring(1)
|
||||
}
|
||||
item.text = set.text
|
||||
item.key = set.identifier ?: ""
|
||||
item.currency = "gems"
|
||||
item.value = set.price
|
||||
item.purchaseType = "customizationSet"
|
||||
if (set.customizations.firstOrNull()?.type == "background") {
|
||||
// TODO: Needs a way to be translated.
|
||||
item.notes = "Get all three Backgrounds in this bundle."
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.models.shops
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.BaseObject
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.inventory.ItemEvent
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import java.util.Date
|
||||
|
||||
open class ShopItem : RealmObject(), BaseObject {
|
||||
@PrimaryKey
|
||||
var key: String = ""
|
||||
var text: String? = ""
|
||||
var notes: String? = ""
|
||||
|
||||
@SerializedName("class")
|
||||
var imageName: String? = null
|
||||
get() {
|
||||
return if (field != null) {
|
||||
if (field!!.contains(" ")) {
|
||||
field!!.split(" ".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[1]
|
||||
} else {
|
||||
field
|
||||
}
|
||||
} else {
|
||||
"shop_$key"
|
||||
}
|
||||
}
|
||||
|
||||
var value: Int = 0
|
||||
var locked: Boolean = false
|
||||
var isLimited: Boolean = false
|
||||
var currency: String? = null
|
||||
var purchaseType: String = ""
|
||||
var categoryIdentifier: String = ""
|
||||
var limitedNumberLeft: Int? = null
|
||||
var unlockCondition: ShopItemUnlockCondition? = null
|
||||
var path: String? = null
|
||||
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
|
||||
var endDate: Date? = null
|
||||
|
||||
val availableUntil: Date?
|
||||
get() {
|
||||
return endDate ?: event?.end
|
||||
}
|
||||
|
||||
var setImageNames = RealmList<String>()
|
||||
|
||||
val isTypeItem: Boolean
|
||||
get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "armoire" == purchaseType || "potion" == purchaseType || "debuffPotion" == purchaseType || "fortify" == purchaseType
|
||||
|
||||
val isTypeQuest: Boolean
|
||||
get() = "quests" == purchaseType
|
||||
|
||||
val isTypeGear: Boolean
|
||||
get() = "gear" == purchaseType
|
||||
|
||||
val isTypeAnimal: Boolean
|
||||
get() = "pets" == purchaseType || "mounts" == purchaseType
|
||||
|
||||
val canPurchaseBulk: Boolean
|
||||
get() = "eggs" == purchaseType || "hatchingPotions" == purchaseType || "food" == purchaseType || "gems" == purchaseType
|
||||
|
||||
fun canAfford(
|
||||
user: User?,
|
||||
quantity: Int,
|
||||
): Boolean =
|
||||
when (currency) {
|
||||
"gold" -> (value * quantity) <= (user?.stats?.gp ?: 0.0)
|
||||
"gems" -> (value * quantity) <= (user?.gemCount ?: 0)
|
||||
"hourglasses" -> (value * quantity) <= (user?.hourglassCount ?: 0)
|
||||
else -> true
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if (other != null && ShopItem::class.java.isAssignableFrom(other.javaClass)) {
|
||||
val otherItem = other as? ShopItem
|
||||
return this.key == otherItem?.key
|
||||
}
|
||||
return super.equals(other)
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return this.key.hashCode()
|
||||
}
|
||||
|
||||
fun shortLockedReason(context: Context): String? {
|
||||
return when {
|
||||
unlockCondition != null -> {
|
||||
unlockCondition?.shortReadableUnlockCondition(context)
|
||||
}
|
||||
previous != null -> {
|
||||
try {
|
||||
val thisNumber = Character.getNumericValue(key.last())
|
||||
context.getString(R.string.unlock_previous_short, thisNumber - 1)
|
||||
} catch (e: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
level != null -> {
|
||||
context.getString(R.string.level_unabbreviated, level ?: 0)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
fun lockedReason(context: Context): String? {
|
||||
return when {
|
||||
unlockCondition != null -> {
|
||||
unlockCondition?.readableUnlockCondition(context)
|
||||
}
|
||||
previous != null -> {
|
||||
try {
|
||||
val thisNumber = Character.getNumericValue(key.last())
|
||||
context.getString(R.string.unlock_previous, thisNumber - 1)
|
||||
} catch (e: NumberFormatException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
level != null -> {
|
||||
context.getString(R.string.unlock_level, level ?: 0)
|
||||
}
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val GEM_FOR_GOLD = "gem"
|
||||
|
||||
fun makeGemItem(res: Resources?): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = GEM_FOR_GOLD
|
||||
item.text = res?.getString(R.string.gem_shop) ?: ""
|
||||
item.notes = res?.getString(R.string.gem_for_gold_description) ?: ""
|
||||
item.imageName = "gem_shop"
|
||||
item.value = 20
|
||||
item.currency = "gold"
|
||||
item.purchaseType = "gems"
|
||||
item.pinType = "gem"
|
||||
item.path = "special.gems"
|
||||
return item
|
||||
}
|
||||
|
||||
fun makeFortifyItem(res: Resources?): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = "fortify"
|
||||
item.text = res?.getString(R.string.fortify_shop) ?: ""
|
||||
item.notes = res?.getString(R.string.fortify_shop_description) ?: ""
|
||||
item.imageName = "inventory_special_fortify"
|
||||
item.value = 4
|
||||
item.currency = "gems"
|
||||
item.pinType = "fortify"
|
||||
item.path = "special.fortify"
|
||||
item.purchaseType = "fortify"
|
||||
return item
|
||||
}
|
||||
|
||||
fun fromCustomization(
|
||||
customization: Customization,
|
||||
userSize: String?,
|
||||
hairColor: String?,
|
||||
): ShopItem {
|
||||
val item = ShopItem()
|
||||
item.key = customization.identifier ?: ""
|
||||
item.text = customization.text
|
||||
item.currency = "gems"
|
||||
item.notes = customization.notes
|
||||
item.value = customization.price ?: 0
|
||||
item.path = customization.path
|
||||
item.unlockPath = customization.unlockPath
|
||||
item.pinType = customization.type
|
||||
if (customization.type == "background") {
|
||||
item.purchaseType = "background"
|
||||
item.imageName = customization.getImageName(userSize, hairColor)
|
||||
} else {
|
||||
item.purchaseType = "customization"
|
||||
item.imageName = customization.getIconName(userSize, hairColor)
|
||||
}
|
||||
return item
|
||||
}
|
||||
|
||||
fun fromCustomizationSet(
|
||||
set: CustomizationSet,
|
||||
additionalSetItems: List<Customization>?,
|
||||
userSize: String?,
|
||||
hairColor: String?,
|
||||
): ShopItem {
|
||||
val item = ShopItem()
|
||||
var path = ""
|
||||
for (customization in set.customizations) {
|
||||
path = path + "," + customization.unlockPath
|
||||
item.setImageNames.add(customization.getIconName(userSize, hairColor))
|
||||
}
|
||||
for (customization in additionalSetItems ?: emptyList()) {
|
||||
path = path + "," + customization.unlockPath
|
||||
item.setImageNames.add(customization.getIconName(userSize, hairColor))
|
||||
}
|
||||
if (path.isEmpty()) {
|
||||
item.unlockPath = path
|
||||
} else {
|
||||
item.unlockPath = path.substring(1)
|
||||
}
|
||||
item.text = set.text
|
||||
item.key = set.identifier ?: ""
|
||||
item.currency = "gems"
|
||||
item.value = set.price
|
||||
item.purchaseType = "customizationSet"
|
||||
if (set.customizations.firstOrNull()?.type == "background") {
|
||||
// TODO: Needs a way to be translated.
|
||||
item.notes = "Get all three Backgrounds in this bundle."
|
||||
}
|
||||
return item
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -23,9 +23,8 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
newContext: Context,
|
||||
userID: String,
|
||||
private val openTaskDisabled: Boolean,
|
||||
private val taskActionsDisabled: Boolean
|
||||
private val taskActionsDisabled: Boolean,
|
||||
) : BaseTasksRecyclerViewAdapter<BindableViewHolder<Task>>(TaskType.HABIT, viewModel, layoutResource, newContext, userID) {
|
||||
|
||||
val taskList: MutableList<Task>
|
||||
get() = content?.map { t -> t }?.toMutableList() ?: mutableListOf()
|
||||
|
||||
|
|
@ -44,7 +43,10 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
}
|
||||
}
|
||||
|
||||
fun addTaskUnder(taskToAdd: Task, taskAbove: Task?): Int {
|
||||
fun addTaskUnder(
|
||||
taskToAdd: Task,
|
||||
taskAbove: Task?,
|
||||
): Int {
|
||||
val position = content?.indexOfFirst { t -> t.id == taskAbove?.id } ?: 0
|
||||
|
||||
content?.add(position + 1, taskToAdd)
|
||||
|
|
@ -53,23 +55,31 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
return position
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BindableViewHolder<Task> {
|
||||
val viewHolder: BindableViewHolder<Task> = when (viewType) {
|
||||
TYPE_HABIT -> HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, {}, null)
|
||||
TYPE_DAILY -> DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, { }, null)
|
||||
TYPE_TODO -> TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, {}, null)
|
||||
TYPE_REWARD -> RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, { }, null)
|
||||
TYPE_ADD_ITEM -> AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), onAddItem)
|
||||
else -> DividerViewHolder(getContentView(parent, R.layout.challenge_task_divider))
|
||||
}
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): BindableViewHolder<Task> {
|
||||
val viewHolder: BindableViewHolder<Task> =
|
||||
when (viewType) {
|
||||
TYPE_HABIT ->
|
||||
HabitViewHolder(getContentView(parent, R.layout.habit_item_card), { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, {}, null)
|
||||
TYPE_DAILY ->
|
||||
DailyViewHolder(getContentView(parent, R.layout.daily_item_card), { _, _ -> }, { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, { }, null)
|
||||
TYPE_TODO ->
|
||||
TodoViewHolder(getContentView(parent, R.layout.todo_item_card), { _, _ -> }, { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, {}, null)
|
||||
TYPE_REWARD ->
|
||||
RewardViewHolder(getContentView(parent, R.layout.reward_item_card), { _, _ -> }, { task, _ ->
|
||||
onTaskOpen?.invoke(task)
|
||||
}, { }, null)
|
||||
TYPE_ADD_ITEM -> AddItemViewHolder(getContentView(parent, R.layout.challenge_add_task_item), onAddItem)
|
||||
else -> DividerViewHolder(getContentView(parent, R.layout.challenge_task_divider))
|
||||
}
|
||||
|
||||
(viewHolder as? BaseTaskViewHolder)?.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
return viewHolder
|
||||
|
|
@ -99,9 +109,8 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
|
||||
inner class AddItemViewHolder internal constructor(
|
||||
itemView: View,
|
||||
private val callback: ((Task) -> Unit)?
|
||||
private val callback: ((Task) -> Unit)?,
|
||||
) : BindableViewHolder<Task>(itemView) {
|
||||
|
||||
private val addBtn: Button = itemView.findViewById(R.id.btn_add_task)
|
||||
private var newTask: Task? = null
|
||||
|
||||
|
|
@ -113,7 +122,7 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String
|
||||
displayMode: String,
|
||||
) {
|
||||
this.newTask = data
|
||||
addBtn.text = data.text
|
||||
|
|
@ -121,13 +130,12 @@ class ChallengeTasksRecyclerViewAdapter(
|
|||
}
|
||||
|
||||
private class DividerViewHolder(itemView: View) : BindableViewHolder<Task>(itemView) {
|
||||
|
||||
private val dividerName: TextView = itemView.findViewById(R.id.divider_name)
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String
|
||||
displayMode: String,
|
||||
) {
|
||||
dividerName.text = data.text
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import com.habitrpg.android.habitica.ui.viewHolders.tasks.DailyViewHolder
|
|||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
|
||||
class DailiesRecyclerViewHolder(layoutResource: Int, viewModel: TasksViewModel) : RealmBaseTasksRecyclerViewAdapter(layoutResource, viewModel) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder {
|
||||
return if (viewType == 0) {
|
||||
DailyViewHolder(
|
||||
getContentView(parent),
|
||||
|
|
@ -21,7 +23,7 @@ class DailiesRecyclerViewHolder(layoutResource: Int, viewModel: TasksViewModel)
|
|||
task ->
|
||||
brokenTaskEvents?.invoke(task)
|
||||
},
|
||||
viewModel
|
||||
viewModel,
|
||||
)
|
||||
} else {
|
||||
super.onCreateViewHolder(parent, viewType)
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import com.habitrpg.android.habitica.ui.viewHolders.tasks.HabitViewHolder
|
|||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
|
||||
class HabitsRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel) : RealmBaseTasksRecyclerViewAdapter(layoutResource, viewModel) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder {
|
||||
return if (viewType == 0) {
|
||||
HabitViewHolder(
|
||||
getContentView(parent),
|
||||
|
|
@ -18,7 +20,7 @@ class HabitsRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel)
|
|||
{ task ->
|
||||
brokenTaskEvents?.invoke(task)
|
||||
},
|
||||
viewModel
|
||||
viewModel,
|
||||
)
|
||||
} else {
|
||||
super.onCreateViewHolder(parent, viewType)
|
||||
|
|
|
|||
|
|
@ -1,155 +1,161 @@
|
|||
package com.habitrpg.android.habitica.ui.adapter.tasks
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class RewardsRecyclerViewAdapter(
|
||||
private var customRewards: List<Task>?,
|
||||
private val layoutResource: Int,
|
||||
val viewModel: TasksViewModel
|
||||
) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), TaskRecyclerViewAdapter {
|
||||
override var user: User? = null
|
||||
set(value) {
|
||||
if (field?.versionNumber == value?.versionNumber) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
override var showAdventureGuide: Boolean = false
|
||||
private var inAppRewards: List<ShopItem>? = null
|
||||
|
||||
override var errorButtonEvents: ((String) -> Unit)? = null
|
||||
override var taskScoreEvents: ((Task, TaskDirection) -> Unit)? = null
|
||||
override var checklistItemScoreEvents: ((Task, ChecklistItem) -> Unit)? = null
|
||||
override var taskOpenEvents: ((Task, View) -> Unit)? = null
|
||||
override var brokenTaskEvents: ((Task) -> Unit)? = null
|
||||
override var adventureGuideOpenEvents: ((Boolean) -> Unit)? = null
|
||||
var purchaseCardEvents: ((ShopItem) -> Unit)? = null
|
||||
var onShowPurchaseDialog: ((ShopItem, Boolean) -> Unit)? = null
|
||||
var goldGemsLeft: Int? = null
|
||||
|
||||
override var taskDisplayMode: String = "standard"
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private val inAppRewardCount: Int
|
||||
get() {
|
||||
// if (inAppRewards?.isValid != true) return 0
|
||||
return inAppRewards?.size ?: 0
|
||||
}
|
||||
|
||||
private val customRewardCount: Int
|
||||
get() {
|
||||
// if (customRewards?.isValid != true) return 0
|
||||
return customRewards?.size ?: 0
|
||||
}
|
||||
|
||||
private fun getContentView(parent: ViewGroup): View {
|
||||
return LayoutInflater.from(parent.context).inflate(layoutResource, parent, false)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return if (viewType == VIEWTYPE_CUSTOM_REWARD) {
|
||||
RewardViewHolder(
|
||||
getContentView(parent),
|
||||
{ task, direction ->
|
||||
if (task.value <= (user?.stats?.gp ?: 0.0)) {
|
||||
taskScoreEvents?.invoke(task, direction)
|
||||
}
|
||||
},
|
||||
{ task, view ->
|
||||
taskOpenEvents?.invoke(task, view)
|
||||
},
|
||||
{
|
||||
task ->
|
||||
brokenTaskEvents?.invoke(task)
|
||||
},
|
||||
viewModel
|
||||
)
|
||||
} else {
|
||||
val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false))
|
||||
viewHolder.purchaseCardAction = { purchaseCardEvents?.invoke(it) }
|
||||
viewHolder.onShowPurchaseDialog = onShowPurchaseDialog
|
||||
viewHolder
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (customRewards != null && position < customRewardCount) {
|
||||
val reward = customRewards?.get(position) ?: return
|
||||
val gold = user?.stats?.gp ?: 0.0
|
||||
(holder as? RewardViewHolder)?.isLocked = !viewModel.canScoreTask(reward)
|
||||
(holder as? RewardViewHolder)?.bind(reward, position, reward.value <= gold, taskDisplayMode, viewModel.ownerID.value)
|
||||
} else if (inAppRewards != null) {
|
||||
val item = inAppRewards?.get(position - customRewardCount) ?: return
|
||||
if (holder is ShopItemViewHolder) {
|
||||
if (item.key == "gem") {
|
||||
holder.limitedNumberLeft = goldGemsLeft
|
||||
}
|
||||
holder.bind(item, item.canAfford(user, 1), 0)
|
||||
holder.isPinned = true
|
||||
holder.hidePinIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if ((customRewards != null && position < customRewardCount) || (customRewardCount == 0 && inAppRewardCount == 0)) {
|
||||
VIEWTYPE_CUSTOM_REWARD
|
||||
} else {
|
||||
VIEWTYPE_IN_APP_REWARD
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateUnfilteredData(data: List<Task>?) {
|
||||
updateData(data)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
var rewardCount = customRewardCount
|
||||
if (viewModel.isPersonalBoard) {
|
||||
rewardCount += inAppRewardCount
|
||||
}
|
||||
return rewardCount
|
||||
}
|
||||
|
||||
fun updateData(tasks: List<Task>?) {
|
||||
this.customRewards = tasks
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun updateItemRewards(items: List<ShopItem>) {
|
||||
if (items.isNotEmpty()) {
|
||||
if (Task::class.java.isAssignableFrom(items.first().javaClass)) {
|
||||
// this catches a weird bug where the observable gets a list of tasks for no apparent reason.
|
||||
return
|
||||
}
|
||||
}
|
||||
this.inAppRewards = items
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun filter() { /* no-on */ }
|
||||
|
||||
companion object {
|
||||
private const val VIEWTYPE_CUSTOM_REWARD = 0
|
||||
private const val VIEWTYPE_IN_APP_REWARD = 3
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.adapter.tasks
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.ShopItemViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.tasks.RewardViewHolder
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class RewardsRecyclerViewAdapter(
|
||||
private var customRewards: List<Task>?,
|
||||
private val layoutResource: Int,
|
||||
val viewModel: TasksViewModel,
|
||||
) : BaseRecyclerViewAdapter<Task, RecyclerView.ViewHolder>(), TaskRecyclerViewAdapter {
|
||||
override var user: User? = null
|
||||
set(value) {
|
||||
if (field?.versionNumber == value?.versionNumber) {
|
||||
return
|
||||
}
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
override var showAdventureGuide: Boolean = false
|
||||
private var inAppRewards: List<ShopItem>? = null
|
||||
|
||||
override var errorButtonEvents: ((String) -> Unit)? = null
|
||||
override var taskScoreEvents: ((Task, TaskDirection) -> Unit)? = null
|
||||
override var checklistItemScoreEvents: ((Task, ChecklistItem) -> Unit)? = null
|
||||
override var taskOpenEvents: ((Task, View) -> Unit)? = null
|
||||
override var brokenTaskEvents: ((Task) -> Unit)? = null
|
||||
override var adventureGuideOpenEvents: ((Boolean) -> Unit)? = null
|
||||
var purchaseCardEvents: ((ShopItem) -> Unit)? = null
|
||||
var onShowPurchaseDialog: ((ShopItem, Boolean) -> Unit)? = null
|
||||
var goldGemsLeft: Int? = null
|
||||
|
||||
override var taskDisplayMode: String = "standard"
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
private val inAppRewardCount: Int
|
||||
get() {
|
||||
// if (inAppRewards?.isValid != true) return 0
|
||||
return inAppRewards?.size ?: 0
|
||||
}
|
||||
|
||||
private val customRewardCount: Int
|
||||
get() {
|
||||
// if (customRewards?.isValid != true) return 0
|
||||
return customRewards?.size ?: 0
|
||||
}
|
||||
|
||||
private fun getContentView(parent: ViewGroup): View {
|
||||
return LayoutInflater.from(parent.context).inflate(layoutResource, parent, false)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder {
|
||||
return if (viewType == VIEWTYPE_CUSTOM_REWARD) {
|
||||
RewardViewHolder(
|
||||
getContentView(parent),
|
||||
{ task, direction ->
|
||||
if (task.value <= (user?.stats?.gp ?: 0.0)) {
|
||||
taskScoreEvents?.invoke(task, direction)
|
||||
}
|
||||
},
|
||||
{ task, view ->
|
||||
taskOpenEvents?.invoke(task, view)
|
||||
},
|
||||
{
|
||||
task ->
|
||||
brokenTaskEvents?.invoke(task)
|
||||
},
|
||||
viewModel,
|
||||
)
|
||||
} else {
|
||||
val viewHolder = ShopItemViewHolder(LayoutInflater.from(parent.context).inflate(R.layout.row_shopitem, parent, false))
|
||||
viewHolder.purchaseCardAction = { purchaseCardEvents?.invoke(it) }
|
||||
viewHolder.onShowPurchaseDialog = onShowPurchaseDialog
|
||||
viewHolder
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(
|
||||
holder: RecyclerView.ViewHolder,
|
||||
position: Int,
|
||||
) {
|
||||
if (customRewards != null && position < customRewardCount) {
|
||||
val reward = customRewards?.get(position) ?: return
|
||||
val gold = user?.stats?.gp ?: 0.0
|
||||
(holder as? RewardViewHolder)?.isLocked = !viewModel.canScoreTask(reward)
|
||||
(holder as? RewardViewHolder)?.bind(reward, position, reward.value <= gold, taskDisplayMode, viewModel.ownerID.value)
|
||||
} else if (inAppRewards != null) {
|
||||
val item = inAppRewards?.get(position - customRewardCount) ?: return
|
||||
if (holder is ShopItemViewHolder) {
|
||||
if (item.key == "gem") {
|
||||
holder.limitedNumberLeft = goldGemsLeft
|
||||
}
|
||||
holder.bind(item, item.canAfford(user, 1), 0)
|
||||
holder.isPinned = true
|
||||
holder.hidePinIndicator()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if ((customRewards != null && position < customRewardCount) || (customRewardCount == 0 && inAppRewardCount == 0)) {
|
||||
VIEWTYPE_CUSTOM_REWARD
|
||||
} else {
|
||||
VIEWTYPE_IN_APP_REWARD
|
||||
}
|
||||
}
|
||||
|
||||
override fun updateUnfilteredData(data: List<Task>?) {
|
||||
updateData(data)
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
var rewardCount = customRewardCount
|
||||
if (viewModel.isPersonalBoard) {
|
||||
rewardCount += inAppRewardCount
|
||||
}
|
||||
return rewardCount
|
||||
}
|
||||
|
||||
fun updateData(tasks: List<Task>?) {
|
||||
this.customRewards = tasks
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
fun updateItemRewards(items: List<ShopItem>) {
|
||||
if (items.isNotEmpty()) {
|
||||
if (Task::class.java.isAssignableFrom(items.first().javaClass)) {
|
||||
// this catches a weird bug where the observable gets a list of tasks for no apparent reason.
|
||||
return
|
||||
}
|
||||
}
|
||||
this.inAppRewards = items
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun filter() { /* no-on */ }
|
||||
|
||||
companion object {
|
||||
private const val VIEWTYPE_CUSTOM_REWARD = 0
|
||||
private const val VIEWTYPE_IN_APP_REWARD = 3
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,8 +6,10 @@ import com.habitrpg.android.habitica.ui.viewHolders.tasks.TodoViewHolder
|
|||
import com.habitrpg.android.habitica.ui.viewmodels.TasksViewModel
|
||||
|
||||
class TodosRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel) : RealmBaseTasksRecyclerViewAdapter(layoutResource, viewModel) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
override fun onCreateViewHolder(
|
||||
parent: ViewGroup,
|
||||
viewType: Int,
|
||||
): RecyclerView.ViewHolder {
|
||||
return if (viewType == 0) {
|
||||
TodoViewHolder(
|
||||
getContentView(parent),
|
||||
|
|
@ -19,7 +21,7 @@ class TodosRecyclerViewAdapter(layoutResource: Int, viewModel: TasksViewModel) :
|
|||
{ task ->
|
||||
brokenTaskEvents?.invoke(task)
|
||||
},
|
||||
viewModel
|
||||
viewModel,
|
||||
)
|
||||
} else {
|
||||
super.onCreateViewHolder(parent, viewType)
|
||||
|
|
|
|||
|
|
@ -63,7 +63,6 @@ import kotlin.time.toDuration
|
|||
|
||||
@AndroidEntryPoint
|
||||
class NavigationDrawerFragment : DialogFragment() {
|
||||
|
||||
private var binding: DrawerMainBinding? = null
|
||||
|
||||
@Inject
|
||||
|
|
@ -104,14 +103,15 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
val context = context
|
||||
adapter = if (context != null) {
|
||||
NavigationDrawerAdapter(
|
||||
context.getThemeColor(R.attr.colorPrimaryText),
|
||||
context.getThemeColor(R.attr.colorPrimaryOffset)
|
||||
)
|
||||
} else {
|
||||
NavigationDrawerAdapter(0, 0)
|
||||
}
|
||||
adapter =
|
||||
if (context != null) {
|
||||
NavigationDrawerAdapter(
|
||||
context.getThemeColor(R.attr.colorPrimaryText),
|
||||
context.getThemeColor(R.attr.colorPrimaryOffset),
|
||||
)
|
||||
} else {
|
||||
NavigationDrawerAdapter(0, 0)
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
|
|
@ -125,12 +125,15 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? = inflater.inflate(R.layout.drawer_main, container, false) as? ViewGroup
|
||||
|
||||
private var updatingJobs = mutableMapOf<String, Job>()
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding = DrawerMainBinding.bind(view)
|
||||
binding?.avatarView?.configManager = configManager
|
||||
|
|
@ -155,7 +158,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
lifecycleScope.launchCatching {
|
||||
contentRepository.getWorldState()
|
||||
.combine(
|
||||
inventoryRepository.getAvailableLimitedItems()
|
||||
inventoryRepository.getAvailableLimitedItems(),
|
||||
) { state, items -> Pair(state, items) }
|
||||
.collect { pair ->
|
||||
val gearEvent = pair.first.events.firstOrNull { it.gear }
|
||||
|
|
@ -166,7 +169,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
val diff = (gearEvent?.end?.time ?: 0) - Date().time
|
||||
if (diff < (1.toDuration(DurationUnit.HOURS).inWholeMilliseconds)) {
|
||||
1.toDuration(
|
||||
DurationUnit.SECONDS
|
||||
DurationUnit.SECONDS,
|
||||
)
|
||||
} else {
|
||||
1.toDuration(DurationUnit.MINUTES)
|
||||
|
|
@ -205,7 +208,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
R.id.inboxFragment,
|
||||
null,
|
||||
true,
|
||||
preventReselection = false
|
||||
preventReselection = false,
|
||||
)
|
||||
}
|
||||
binding?.settingsButtonWrapper?.setOnClickListener {
|
||||
|
|
@ -213,7 +216,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
R.id.prefsActivity,
|
||||
null,
|
||||
true,
|
||||
preventReselection = false
|
||||
preventReselection = false,
|
||||
)
|
||||
}
|
||||
binding?.notificationsButtonWrapper?.setOnClickListener { startNotificationsActivity() }
|
||||
|
|
@ -223,21 +226,25 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
key: String,
|
||||
endingCondition: () -> Boolean,
|
||||
delayFunc: () -> Duration,
|
||||
function: () -> Unit
|
||||
function: () -> Unit,
|
||||
) {
|
||||
function()
|
||||
if (updatingJobs[key]?.isActive == true) {
|
||||
updatingJobs[key]?.cancel()
|
||||
}
|
||||
updatingJobs[key] = lifecycleScope.launch(Dispatchers.Main) {
|
||||
while (endingCondition()) {
|
||||
function()
|
||||
delay(delayFunc())
|
||||
updatingJobs[key] =
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
while (endingCondition()) {
|
||||
function()
|
||||
delay(delayFunc())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSeasonalMenuEntries(gearEvent: WorldStateEvent?, items: List<Item>) {
|
||||
private fun updateSeasonalMenuEntries(
|
||||
gearEvent: WorldStateEvent?,
|
||||
items: List<Item>,
|
||||
) {
|
||||
val market = getItemWithIdentifier(SIDEBAR_SHOPS_MARKET) ?: return
|
||||
val item = items.firstOrNull()
|
||||
if (item?.isValid() == true && item.event?.end?.after(Date()) == true) {
|
||||
|
|
@ -283,8 +290,8 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
item.isVisible = false
|
||||
} else {
|
||||
if ((
|
||||
user.stats?.lvl
|
||||
?: 0
|
||||
user.stats?.lvl
|
||||
?: 0
|
||||
) < HabiticaSnackbar.MIN_LEVEL_FOR_SKILLS && (!hasSpecialItems)
|
||||
) {
|
||||
item.pillText = getString(R.string.unlock_lvl_11)
|
||||
|
|
@ -320,11 +327,12 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
context?.let {
|
||||
subscriptionItem?.subtitle =
|
||||
user.purchased?.plan?.dateTerminated?.getRemainingString(it.resources)
|
||||
subscriptionItem?.subtitleTextColor = when {
|
||||
daysDiff <= 2 -> ContextCompat.getColor(it, R.color.red_100)
|
||||
daysDiff <= 7 -> ContextCompat.getColor(it, R.color.brand_400)
|
||||
else -> it.getThemeColor(R.attr.textColorSecondary)
|
||||
}
|
||||
subscriptionItem?.subtitleTextColor =
|
||||
when {
|
||||
daysDiff <= 2 -> ContextCompat.getColor(it, R.color.red_100)
|
||||
daysDiff <= 7 -> ContextCompat.getColor(it, R.color.brand_400)
|
||||
else -> it.getThemeColor(R.attr.textColorSecondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (user.isSubscribed) {
|
||||
|
|
@ -370,29 +378,29 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
HabiticaDrawerItem(
|
||||
R.id.tasksFragment,
|
||||
SIDEBAR_TASKS,
|
||||
context.getString(R.string.sidebar_tasks)
|
||||
)
|
||||
context.getString(R.string.sidebar_tasks),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.skillsFragment,
|
||||
SIDEBAR_SKILLS,
|
||||
context.getString(R.string.sidebar_skills)
|
||||
)
|
||||
context.getString(R.string.sidebar_skills),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.statsFragment,
|
||||
SIDEBAR_STATS,
|
||||
context.getString(R.string.sidebar_stats)
|
||||
)
|
||||
context.getString(R.string.sidebar_stats),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.achievementsFragment,
|
||||
SIDEBAR_ACHIEVEMENTS,
|
||||
context.getString(R.string.sidebar_achievements)
|
||||
)
|
||||
context.getString(R.string.sidebar_achievements),
|
||||
),
|
||||
)
|
||||
|
||||
items.add(
|
||||
|
|
@ -400,45 +408,46 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
0,
|
||||
SIDEBAR_INVENTORY,
|
||||
context.getString(R.string.sidebar_shops),
|
||||
isHeader = true
|
||||
)
|
||||
isHeader = true,
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.marketFragment,
|
||||
SIDEBAR_SHOPS_MARKET,
|
||||
context.getString(R.string.market)
|
||||
)
|
||||
context.getString(R.string.market),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.questShopFragment,
|
||||
SIDEBAR_SHOPS_QUEST,
|
||||
context.getString(R.string.questShop)
|
||||
)
|
||||
context.getString(R.string.questShop),
|
||||
),
|
||||
)
|
||||
if (configManager.enableCustomizationShop()) {
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.customizationsShopFragment,
|
||||
SIDEBAR_SHOPS_CUSTOMIZATIONS,
|
||||
context.getString(R.string.customizations)
|
||||
)
|
||||
context.getString(R.string.customizations),
|
||||
),
|
||||
)
|
||||
}
|
||||
val seasonalShopEntry = HabiticaDrawerItem(
|
||||
R.id.seasonalShopFragment,
|
||||
SIDEBAR_SHOPS_SEASONAL,
|
||||
context.getString(R.string.seasonalShop)
|
||||
)
|
||||
val seasonalShopEntry =
|
||||
HabiticaDrawerItem(
|
||||
R.id.seasonalShopFragment,
|
||||
SIDEBAR_SHOPS_SEASONAL,
|
||||
context.getString(R.string.seasonalShop),
|
||||
)
|
||||
seasonalShopEntry.isVisible = false
|
||||
items.add(seasonalShopEntry)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.timeTravelersShopFragment,
|
||||
SIDEBAR_SHOPS_TIMETRAVEL,
|
||||
context.getString(R.string.timeTravelers)
|
||||
)
|
||||
context.getString(R.string.timeTravelers),
|
||||
),
|
||||
)
|
||||
|
||||
items.add(
|
||||
|
|
@ -446,73 +455,73 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
0,
|
||||
SIDEBAR_INVENTORY,
|
||||
context.getString(R.string.sidebar_section_inventory),
|
||||
isHeader = true
|
||||
)
|
||||
isHeader = true,
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.avatarOverviewFragment,
|
||||
SIDEBAR_AVATAR,
|
||||
context.getString(R.string.sidebar_avatar)
|
||||
)
|
||||
context.getString(R.string.sidebar_avatar),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.equipmentOverviewFragment,
|
||||
SIDEBAR_EQUIPMENT,
|
||||
context.getString(R.string.sidebar_equipment)
|
||||
)
|
||||
context.getString(R.string.sidebar_equipment),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.itemsFragment,
|
||||
SIDEBAR_ITEMS,
|
||||
context.getString(R.string.sidebar_items)
|
||||
)
|
||||
context.getString(R.string.sidebar_items),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.stableFragment,
|
||||
SIDEBAR_STABLE,
|
||||
context.getString(R.string.sidebar_stable)
|
||||
)
|
||||
context.getString(R.string.sidebar_stable),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.gemPurchaseActivity,
|
||||
SIDEBAR_GEMS,
|
||||
context.getString(R.string.sidebar_gems)
|
||||
)
|
||||
context.getString(R.string.sidebar_gems),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.subscriptionPurchaseActivity,
|
||||
SIDEBAR_SUBSCRIPTION,
|
||||
context.getString(R.string.sidebar_subscription)
|
||||
)
|
||||
context.getString(R.string.sidebar_subscription),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
0,
|
||||
SIDEBAR_SOCIAL,
|
||||
context.getString(R.string.sidebar_section_social),
|
||||
isHeader = true
|
||||
)
|
||||
isHeader = true,
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.partyFragment,
|
||||
SIDEBAR_PARTY,
|
||||
context.getString(R.string.sidebar_party)
|
||||
)
|
||||
context.getString(R.string.sidebar_party),
|
||||
),
|
||||
)
|
||||
if (!configManager.hideChallenges()) {
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.challengesOverviewFragment,
|
||||
SIDEBAR_CHALLENGES,
|
||||
context.getString(R.string.sidebar_challenges)
|
||||
)
|
||||
context.getString(R.string.sidebar_challenges),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -521,29 +530,29 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
0,
|
||||
SIDEBAR_ABOUT_HEADER,
|
||||
context.getString(R.string.sidebar_about),
|
||||
isHeader = true
|
||||
)
|
||||
isHeader = true,
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.newsFragment,
|
||||
SIDEBAR_NEWS,
|
||||
context.getString(R.string.sidebar_news)
|
||||
)
|
||||
context.getString(R.string.sidebar_news),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.supportMainFragment,
|
||||
SIDEBAR_HELP,
|
||||
context.getString(R.string.sidebar_help)
|
||||
)
|
||||
context.getString(R.string.sidebar_help),
|
||||
),
|
||||
)
|
||||
items.add(
|
||||
HabiticaDrawerItem(
|
||||
R.id.aboutFragment,
|
||||
SIDEBAR_ABOUT,
|
||||
context.getString(R.string.sidebar_about)
|
||||
)
|
||||
context.getString(R.string.sidebar_about),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
|
|
@ -565,7 +574,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
transitionId: Int?,
|
||||
bundle: Bundle? = null,
|
||||
openSelection: Boolean = true,
|
||||
preventReselection: Boolean = true
|
||||
preventReselection: Boolean = true,
|
||||
) {
|
||||
if (!isTabletUI) {
|
||||
closeDrawer()
|
||||
|
|
@ -605,7 +614,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
if (it.resultCode == Activity.RESULT_OK) {
|
||||
(activity as? MainActivity)?.notificationsViewModel?.click(
|
||||
it.data?.getStringExtra("notificationId") ?: "",
|
||||
MainNavigationController
|
||||
MainNavigationController,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -619,7 +628,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
fun setUp(
|
||||
fragmentId: Int,
|
||||
drawerLayout: DrawerLayout,
|
||||
viewModel: NotificationsViewModel
|
||||
viewModel: NotificationsViewModel,
|
||||
) {
|
||||
fragmentContainerView = activity?.findViewById(fragmentId)
|
||||
this.drawerLayout = drawerLayout
|
||||
|
|
@ -705,11 +714,12 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
|
||||
private fun setNotificationsSeen(allSeen: Boolean) {
|
||||
context?.let {
|
||||
val color = if (allSeen) {
|
||||
ContextCompat.getColor(it, R.color.gray_200)
|
||||
} else {
|
||||
it.getThemeColor(R.attr.colorAccent)
|
||||
}
|
||||
val color =
|
||||
if (allSeen) {
|
||||
ContextCompat.getColor(it, R.color.gray_200)
|
||||
} else {
|
||||
it.getThemeColor(R.attr.colorAccent)
|
||||
}
|
||||
|
||||
val bg = binding?.notificationsBadge?.background as? GradientDrawable
|
||||
bg?.color = ColorStateList.valueOf(color)
|
||||
|
|
@ -723,11 +733,12 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
binding?.messagesBadge?.visibility = View.VISIBLE
|
||||
binding?.messagesBadge?.text = numOfUnreadMessages.toString()
|
||||
context?.let {
|
||||
val color = if (inbox?.hasUserSeenInbox != true) {
|
||||
it.getThemeColor(R.attr.colorAccent)
|
||||
} else {
|
||||
ContextCompat.getColor(it, R.color.gray_200)
|
||||
}
|
||||
val color =
|
||||
if (inbox?.hasUserSeenInbox != true) {
|
||||
it.getThemeColor(R.attr.colorAccent)
|
||||
} else {
|
||||
ContextCompat.getColor(it, R.color.gray_200)
|
||||
}
|
||||
val background = binding?.messagesBadge?.background as? GradientDrawable
|
||||
background?.color = ColorStateList.valueOf(color)
|
||||
binding?.messagesBadge?.setTextColor(ContextCompat.getColor(it, R.color.white))
|
||||
|
|
@ -772,10 +783,11 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
1.toDuration(diff.getMinuteOrSeconds())
|
||||
}) {
|
||||
if (activePromo.isActive) {
|
||||
promotedItem.subtitle = context?.getString(
|
||||
R.string.sale_ends_in,
|
||||
activePromo.endDate.getShortRemainingString()
|
||||
)
|
||||
promotedItem.subtitle =
|
||||
context?.getString(
|
||||
R.string.sale_ends_in,
|
||||
activePromo.endDate.getShortRemainingString(),
|
||||
)
|
||||
adapter.updateItem(promotedItem)
|
||||
} else {
|
||||
promotedItem.subtitle = null
|
||||
|
|
@ -790,7 +802,6 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
}
|
||||
|
||||
companion object {
|
||||
|
||||
const val SIDEBAR_TASKS = "tasks"
|
||||
const val SIDEBAR_SKILLS = "skills"
|
||||
const val SIDEBAR_STATS = "stats"
|
||||
|
|
|
|||
|
|
@ -20,48 +20,55 @@ import javax.inject.Inject
|
|||
|
||||
@AndroidEntryPoint
|
||||
class NewsFragment : BaseMainFragment<FragmentNewsBinding>() {
|
||||
|
||||
@Inject
|
||||
lateinit var hostConfig: HostConfig
|
||||
|
||||
override var binding: FragmentNewsBinding? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentNewsBinding {
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
): FragmentNewsBinding {
|
||||
return FragmentNewsBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
this.hidesToolbar = true
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
private val webviewClient = object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?
|
||||
): Boolean {
|
||||
if (request?.url?.path == "/static/new-stuff") {
|
||||
view?.loadUrl(request.url.toString())
|
||||
} else {
|
||||
request?.url?.let { MainNavigationController.navigate(it) }
|
||||
private val webviewClient =
|
||||
object : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(
|
||||
view: WebView?,
|
||||
request: WebResourceRequest?,
|
||||
): Boolean {
|
||||
if (request?.url?.path == "/static/new-stuff") {
|
||||
view?.loadUrl(request.url.toString())
|
||||
} else {
|
||||
request?.url?.let { MainNavigationController.navigate(it) }
|
||||
}
|
||||
return true
|
||||
}
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val webSettings = binding?.newsWebview?.settings
|
||||
webSettings?.javaScriptEnabled = true
|
||||
webSettings?.domStorageEnabled = true
|
||||
binding?.newsWebview?.webViewClient = webviewClient
|
||||
binding?.newsWebview?.webChromeClient = object : WebChromeClient() {
|
||||
}
|
||||
binding?.newsWebview?.webChromeClient =
|
||||
object : WebChromeClient() {
|
||||
}
|
||||
binding?.newsWebview?.loadUrl("${hostConfig.address}/static/new-stuff")
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -52,11 +52,13 @@ import javax.inject.Inject
|
|||
class AvatarCustomizationFragment :
|
||||
BaseMainFragment<FragmentRefreshRecyclerviewBinding>(),
|
||||
SwipeRefreshLayout.OnRefreshListener {
|
||||
|
||||
private var filterMenuItem: MenuItem? = null
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
): FragmentRefreshRecyclerviewBinding {
|
||||
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +84,7 @@ class AvatarCustomizationFragment :
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
showsBackButton = true
|
||||
adapter.onCustomizationSelected = { customization ->
|
||||
|
|
@ -96,7 +98,7 @@ class AvatarCustomizationFragment :
|
|||
userRepository.useCustomization(
|
||||
customization.type ?: "",
|
||||
customization.category,
|
||||
customization.identifier ?: ""
|
||||
customization.identifier ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -115,7 +117,10 @@ class AvatarCustomizationFragment :
|
|||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
arguments?.let {
|
||||
val args = AvatarCustomizationFragmentArgs.fromBundle(it)
|
||||
|
|
@ -158,7 +163,10 @@ class AvatarCustomizationFragment :
|
|||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
override fun onCreateOptionsMenu(
|
||||
menu: Menu,
|
||||
inflater: MenuInflater,
|
||||
) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_list_customizations, menu)
|
||||
|
||||
|
|
@ -215,7 +223,7 @@ class AvatarCustomizationFragment :
|
|||
displayedCustomizations.reversed()
|
||||
} else {
|
||||
displayedCustomizations
|
||||
}
|
||||
},
|
||||
)
|
||||
} else {
|
||||
adapter.setCustomizations(
|
||||
|
|
@ -223,7 +231,7 @@ class AvatarCustomizationFragment :
|
|||
customizations.reversed()
|
||||
} else {
|
||||
customizations
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -241,7 +249,7 @@ class AvatarCustomizationFragment :
|
|||
private fun shouldSkip(
|
||||
filter: CustomizationFilter,
|
||||
ownedCustomizations: List<OwnedCustomization>,
|
||||
customization: Customization
|
||||
customization: Customization,
|
||||
): Boolean {
|
||||
return if (filter.onlyPurchased && ownedCustomizations.find { it.key == customization.identifier } == null) {
|
||||
true
|
||||
|
|
@ -266,22 +274,24 @@ class AvatarCustomizationFragment :
|
|||
return
|
||||
}
|
||||
val prefs = user.preferences
|
||||
val activeCustomization = when (this.type) {
|
||||
"skin" -> prefs?.skin
|
||||
"shirt" -> prefs?.shirt
|
||||
"background" -> prefs?.background
|
||||
"chair" -> prefs?.chair
|
||||
"hair" -> when (this.category) {
|
||||
"bangs" -> prefs?.hair?.bangs.toString()
|
||||
"base" -> prefs?.hair?.base.toString()
|
||||
"color" -> prefs?.hair?.color
|
||||
"flower" -> prefs?.hair?.flower.toString()
|
||||
"beard" -> prefs?.hair?.beard.toString()
|
||||
"mustache" -> prefs?.hair?.mustache.toString()
|
||||
val activeCustomization =
|
||||
when (this.type) {
|
||||
"skin" -> prefs?.skin
|
||||
"shirt" -> prefs?.shirt
|
||||
"background" -> prefs?.background
|
||||
"chair" -> prefs?.chair
|
||||
"hair" ->
|
||||
when (this.category) {
|
||||
"bangs" -> prefs?.hair?.bangs.toString()
|
||||
"base" -> prefs?.hair?.base.toString()
|
||||
"color" -> prefs?.hair?.color
|
||||
"flower" -> prefs?.hair?.flower.toString()
|
||||
"beard" -> prefs?.hair?.beard.toString()
|
||||
"mustache" -> prefs?.hair?.mustache.toString()
|
||||
else -> ""
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
else -> ""
|
||||
}
|
||||
if (activeCustomization != null) {
|
||||
this.activeCustomization = activeCustomization
|
||||
this.adapter.activeCustomization = activeCustomization
|
||||
|
|
@ -340,7 +350,11 @@ class AvatarCustomizationFragment :
|
|||
dialog.show()
|
||||
}
|
||||
|
||||
private fun configureMonthFilterButton(button: CheckBox, value: Int, filter: CustomizationFilter) {
|
||||
private fun configureMonthFilterButton(
|
||||
button: CheckBox,
|
||||
value: Int,
|
||||
filter: CustomizationFilter,
|
||||
) {
|
||||
val identifier = value.toString().padStart(2, '0')
|
||||
button.isChecked = filter.months.contains(identifier)
|
||||
button.text
|
||||
|
|
@ -358,4 +372,4 @@ class AvatarCustomizationFragment :
|
|||
currentFilter.value = newFilter
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,284 +1,304 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.inventory.customization
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.map
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentComposeScrollingBinding
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.interactors.ShareAvatarUseCase
|
||||
import com.habitrpg.android.habitica.models.inventory.Equipment
|
||||
import com.habitrpg.android.habitica.ui.activities.BaseActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.theme.colors
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.SegmentedControl
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.AvatarCustomizationOverviewView
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
open class AvatarOverviewFragment :
|
||||
BaseMainFragment<FragmentComposeScrollingBinding>(),
|
||||
AdapterView.OnItemSelectedListener {
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
override var binding: FragmentComposeScrollingBinding? = null
|
||||
|
||||
protected var showCustomization = true
|
||||
|
||||
private val battleGearWeapon = mutableStateOf<Equipment?>(null)
|
||||
private val costumeWeapon = mutableStateOf<Equipment?>(null)
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?
|
||||
): FragmentComposeScrollingBinding {
|
||||
return FragmentComposeScrollingBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
binding?.composeView?.apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
HabiticaTheme {
|
||||
AvatarOverviewView(
|
||||
userViewModel,
|
||||
showCustomization,
|
||||
!showCustomization,
|
||||
battleGearWeapon.value?.twoHanded == true,
|
||||
costumeWeapon.value?.twoHanded == true,
|
||||
{ type, category ->
|
||||
displayCustomizationFragment(type, category)
|
||||
},
|
||||
{ type, category ->
|
||||
displayAvatarEquipmentFragment(type, category)
|
||||
},
|
||||
{ type, equipped, isCostume ->
|
||||
displayEquipmentFragment(type, equipped, isCostume)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userViewModel.user.map { Pair(it?.items?.gear?.equipped?.weapon, it?.items?.gear?.costume?.weapon) }
|
||||
.observe(viewLifecycleOwner) {
|
||||
lifecycleScope.launchCatching {
|
||||
battleGearWeapon.value = it.first?.let { key ->
|
||||
inventoryRepository.getEquipment(
|
||||
key
|
||||
).firstOrNull()
|
||||
}
|
||||
costumeWeapon.value = it.second?.let { key ->
|
||||
inventoryRepository.getEquipment(
|
||||
key
|
||||
).firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
private fun displayCustomizationFragment(type: String, category: String?) {
|
||||
if (appConfigManager.enableCustomizationShop()) {
|
||||
MainNavigationController.navigate(
|
||||
AvatarOverviewFragmentDirections.openComposeAvatarDetail(
|
||||
type,
|
||||
category ?: ""
|
||||
)
|
||||
)
|
||||
} else {
|
||||
MainNavigationController.navigate(
|
||||
AvatarOverviewFragmentDirections.openAvatarDetail(
|
||||
type,
|
||||
category ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayAvatarEquipmentFragment(type: String, category: String?) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarEquipment(type, category ?: ""))
|
||||
}
|
||||
|
||||
private fun displayEquipmentFragment(type: String, equipped: String?, isCostume: Boolean = false) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openEquipmentDetail(type, isCostume, equipped ?: ""))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_share_avatar, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.share_avatar) {
|
||||
userViewModel.user.value?.let {
|
||||
val usecase = ShareAvatarUseCase()
|
||||
lifecycleScope.launchCatching {
|
||||
usecase.callInteractor(
|
||||
ShareAvatarUseCase.RequestValues(
|
||||
requireActivity() as BaseActivity,
|
||||
it,
|
||||
"Check out my avatar on Habitica!",
|
||||
"avatar_customization"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onItemSelected(parent: AdapterView<*>, view: View?, position: Int, id: Long) {
|
||||
val newSize: String = if (position == 0) "slim" else "broad"
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.updateUser("preferences.size", newSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) { /* no-on */
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AvatarOverviewView(
|
||||
userViewModel: MainUserViewModel,
|
||||
showCustomization: Boolean = true,
|
||||
showEquipment: Boolean = true,
|
||||
battleGearTwoHanded: Boolean = false,
|
||||
costumeTwoHanded: Boolean = false,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
onAvatarEquipmentTap: (String, String?) -> Unit,
|
||||
onEquipmentTap: (String, String?, Boolean) -> Unit,
|
||||
) {
|
||||
val user by userViewModel.user.observeAsState()
|
||||
Column(
|
||||
Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(bottom = 16.dp)
|
||||
) {
|
||||
if (showCustomization) {
|
||||
Row(
|
||||
Modifier.padding(horizontal = 12.dp, vertical = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_size),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
SegmentedControl(
|
||||
items = listOf(
|
||||
stringResource(R.string.avatar_size_slim),
|
||||
stringResource(
|
||||
R.string.avatar_size_broad
|
||||
)
|
||||
),
|
||||
defaultSelectedItemIndex = if (user?.preferences?.size == "slim") 0 else 1,
|
||||
onItemSelection = {
|
||||
userViewModel.updateUser(
|
||||
"preferences.size",
|
||||
if (it == 0) "slim" else "broad"
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
AvatarCustomizationOverviewView(user?.preferences, user?.items?.gear?.equipped, onCustomizationTap, onAvatarEquipmentTap)
|
||||
}
|
||||
if (showEquipment) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(top = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.equipped),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.equip_automatically),
|
||||
style = HabiticaTheme.typography.body2,
|
||||
color = HabiticaTheme.colors.textPrimary
|
||||
)
|
||||
Switch(checked = user?.preferences?.autoEquip == true, onCheckedChange = {
|
||||
userViewModel.updateUser("preferences.autoEquip", it)
|
||||
})
|
||||
}
|
||||
EquipmentOverviewView(user?.items?.gear?.equipped, battleGearTwoHanded, { type, equipped ->
|
||||
onEquipmentTap(type, equipped, false)
|
||||
})
|
||||
Row(
|
||||
Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(top = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.costume),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.wear_costume),
|
||||
style = HabiticaTheme.typography.body2,
|
||||
color = HabiticaTheme.colors.textPrimary
|
||||
)
|
||||
Switch(checked = user?.preferences?.costume == true, onCheckedChange = {
|
||||
userViewModel.updateUser("preferences.costume", it)
|
||||
})
|
||||
}
|
||||
EquipmentOverviewView(user?.items?.gear?.costume, costumeTwoHanded, { type, equipped ->
|
||||
onEquipmentTap(type, equipped, true)
|
||||
}, modifier = Modifier.alpha(if (user?.preferences?.costume == true) 1.0f else 0.5f))
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.fragments.inventory.customization
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.AdapterView
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material.Switch
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.livedata.observeAsState
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.platform.ViewCompositionStrategy
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.map
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentComposeScrollingBinding
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.interactors.ShareAvatarUseCase
|
||||
import com.habitrpg.android.habitica.models.inventory.Equipment
|
||||
import com.habitrpg.android.habitica.ui.activities.BaseActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.theme.colors
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.SegmentedControl
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.AvatarCustomizationOverviewView
|
||||
import com.habitrpg.android.habitica.ui.views.equipment.EquipmentOverviewView
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.common.habitica.theme.HabiticaTheme
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
open class AvatarOverviewFragment :
|
||||
BaseMainFragment<FragmentComposeScrollingBinding>(),
|
||||
AdapterView.OnItemSelectedListener {
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var appConfigManager: AppConfigManager
|
||||
|
||||
override var binding: FragmentComposeScrollingBinding? = null
|
||||
|
||||
protected var showCustomization = true
|
||||
|
||||
private val battleGearWeapon = mutableStateOf<Equipment?>(null)
|
||||
private val costumeWeapon = mutableStateOf<Equipment?>(null)
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
): FragmentComposeScrollingBinding {
|
||||
return FragmentComposeScrollingBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
val view = super.onCreateView(inflater, container, savedInstanceState)
|
||||
binding?.composeView?.apply {
|
||||
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
|
||||
setContent {
|
||||
HabiticaTheme {
|
||||
AvatarOverviewView(
|
||||
userViewModel,
|
||||
showCustomization,
|
||||
!showCustomization,
|
||||
battleGearWeapon.value?.twoHanded == true,
|
||||
costumeWeapon.value?.twoHanded == true,
|
||||
{ type, category ->
|
||||
displayCustomizationFragment(type, category)
|
||||
},
|
||||
{ type, category ->
|
||||
displayAvatarEquipmentFragment(type, category)
|
||||
},
|
||||
{ type, equipped, isCostume ->
|
||||
displayEquipmentFragment(type, equipped, isCostume)
|
||||
},
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
userViewModel.user.map { Pair(it?.items?.gear?.equipped?.weapon, it?.items?.gear?.costume?.weapon) }
|
||||
.observe(viewLifecycleOwner) {
|
||||
lifecycleScope.launchCatching {
|
||||
battleGearWeapon.value =
|
||||
it.first?.let { key ->
|
||||
inventoryRepository.getEquipment(
|
||||
key,
|
||||
).firstOrNull()
|
||||
}
|
||||
costumeWeapon.value =
|
||||
it.second?.let { key ->
|
||||
inventoryRepository.getEquipment(
|
||||
key,
|
||||
).firstOrNull()
|
||||
}
|
||||
}
|
||||
}
|
||||
return view
|
||||
}
|
||||
|
||||
private fun displayCustomizationFragment(
|
||||
type: String,
|
||||
category: String?,
|
||||
) {
|
||||
if (appConfigManager.enableCustomizationShop()) {
|
||||
MainNavigationController.navigate(
|
||||
AvatarOverviewFragmentDirections.openComposeAvatarDetail(
|
||||
type,
|
||||
category ?: "",
|
||||
),
|
||||
)
|
||||
} else {
|
||||
MainNavigationController.navigate(
|
||||
AvatarOverviewFragmentDirections.openAvatarDetail(
|
||||
type,
|
||||
category ?: "",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun displayAvatarEquipmentFragment(
|
||||
type: String,
|
||||
category: String?,
|
||||
) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openAvatarEquipment(type, category ?: ""))
|
||||
}
|
||||
|
||||
private fun displayEquipmentFragment(
|
||||
type: String,
|
||||
equipped: String?,
|
||||
isCostume: Boolean = false,
|
||||
) {
|
||||
MainNavigationController.navigate(AvatarOverviewFragmentDirections.openEquipmentDetail(type, isCostume, equipped ?: ""))
|
||||
}
|
||||
|
||||
override fun onCreateOptionsMenu(
|
||||
menu: Menu,
|
||||
inflater: MenuInflater,
|
||||
) {
|
||||
super.onCreateOptionsMenu(menu, inflater)
|
||||
inflater.inflate(R.menu.menu_share_avatar, menu)
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == R.id.share_avatar) {
|
||||
userViewModel.user.value?.let {
|
||||
val usecase = ShareAvatarUseCase()
|
||||
lifecycleScope.launchCatching {
|
||||
usecase.callInteractor(
|
||||
ShareAvatarUseCase.RequestValues(
|
||||
requireActivity() as BaseActivity,
|
||||
it,
|
||||
"Check out my avatar on Habitica!",
|
||||
"avatar_customization",
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
override fun onItemSelected(
|
||||
parent: AdapterView<*>,
|
||||
view: View?,
|
||||
position: Int,
|
||||
id: Long,
|
||||
) {
|
||||
val newSize: String = if (position == 0) "slim" else "broad"
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
userRepository.updateUser("preferences.size", newSize)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onNothingSelected(parent: AdapterView<*>) { // no-on
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AvatarOverviewView(
|
||||
userViewModel: MainUserViewModel,
|
||||
showCustomization: Boolean = true,
|
||||
showEquipment: Boolean = true,
|
||||
battleGearTwoHanded: Boolean = false,
|
||||
costumeTwoHanded: Boolean = false,
|
||||
onCustomizationTap: (String, String?) -> Unit,
|
||||
onAvatarEquipmentTap: (String, String?) -> Unit,
|
||||
onEquipmentTap: (String, String?, Boolean) -> Unit,
|
||||
) {
|
||||
val user by userViewModel.user.observeAsState()
|
||||
Column(
|
||||
Modifier
|
||||
.padding(horizontal = 8.dp)
|
||||
.padding(bottom = 16.dp),
|
||||
) {
|
||||
if (showCustomization) {
|
||||
Row(
|
||||
Modifier.padding(horizontal = 12.dp, vertical = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.avatar_size),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
SegmentedControl(
|
||||
items =
|
||||
listOf(
|
||||
stringResource(R.string.avatar_size_slim),
|
||||
stringResource(
|
||||
R.string.avatar_size_broad,
|
||||
),
|
||||
),
|
||||
defaultSelectedItemIndex = if (user?.preferences?.size == "slim") 0 else 1,
|
||||
onItemSelection = {
|
||||
userViewModel.updateUser(
|
||||
"preferences.size",
|
||||
if (it == 0) "slim" else "broad",
|
||||
)
|
||||
},
|
||||
)
|
||||
}
|
||||
AvatarCustomizationOverviewView(user?.preferences, user?.items?.gear?.equipped, onCustomizationTap, onAvatarEquipmentTap)
|
||||
}
|
||||
if (showEquipment) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(top = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.equipped),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.equip_automatically),
|
||||
style = HabiticaTheme.typography.body2,
|
||||
color = HabiticaTheme.colors.textPrimary,
|
||||
)
|
||||
Switch(checked = user?.preferences?.autoEquip == true, onCheckedChange = {
|
||||
userViewModel.updateUser("preferences.autoEquip", it)
|
||||
})
|
||||
}
|
||||
EquipmentOverviewView(user?.items?.gear?.equipped, battleGearTwoHanded, { type, equipped ->
|
||||
onEquipmentTap(type, equipped, false)
|
||||
})
|
||||
Row(
|
||||
Modifier
|
||||
.padding(horizontal = 12.dp)
|
||||
.padding(top = 15.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
) {
|
||||
Text(
|
||||
stringResource(R.string.costume),
|
||||
style = HabiticaTheme.typography.subtitle2,
|
||||
color = HabiticaTheme.colors.textSecondary,
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
Text(
|
||||
stringResource(R.string.wear_costume),
|
||||
style = HabiticaTheme.typography.body2,
|
||||
color = HabiticaTheme.colors.textPrimary,
|
||||
)
|
||||
Switch(checked = user?.preferences?.costume == true, onCheckedChange = {
|
||||
userViewModel.updateUser("preferences.costume", it)
|
||||
})
|
||||
}
|
||||
EquipmentOverviewView(user?.items?.gear?.costume, costumeTwoHanded, { type, equipped ->
|
||||
onEquipmentTap(type, equipped, true)
|
||||
}, modifier = Modifier.alpha(if (user?.preferences?.costume == true) 1.0f else 0.5f))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -12,7 +12,7 @@ class CustomizationsShopFragment : ShopFragment() {
|
|||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
shopIdentifier = Shop.CUSTOMIZATIONS
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
|
|
|
|||
|
|
@ -1,419 +1,432 @@
|
|||
package com.habitrpg.android.habitica.ui.fragments.inventory.shops
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
|
||||
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.models.shops.Shop
|
||||
import com.habitrpg.android.habitica.models.shops.ShopCategory
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.ShopRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.purchases.EventOutcomeSubscriptionBottomSheetFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionBottomSheetFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CurrencyText
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaProgressDialog
|
||||
import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGemsDialog
|
||||
import com.habitrpg.android.habitica.ui.views.shops.PurchaseDialog
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewState
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
open class ShopFragment : BaseMainFragment<FragmentRefreshRecyclerviewBinding>() {
|
||||
|
||||
internal val currencyView: ComposeView by lazy {
|
||||
return@lazy ComposeView(requireContext())
|
||||
}
|
||||
|
||||
var adapter: ShopRecyclerAdapter? = null
|
||||
var shopIdentifier: String? = null
|
||||
var shop: Shop? = null
|
||||
internal val hourglasses = mutableStateOf<Double?>(null)
|
||||
private val gems = mutableStateOf<Double?>(null)
|
||||
private val gold = mutableStateOf<Double?>(null)
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
private var layoutManager: GridLayoutManager? = null
|
||||
|
||||
private var gearCategories: MutableList<ShopCategory>? = null
|
||||
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
override fun createBinding(inflater: LayoutInflater, container: ViewGroup?): FragmentRefreshRecyclerviewBinding {
|
||||
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
this.hidesToolbar = true
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
inventoryRepository.close()
|
||||
socialRepository.close()
|
||||
toolbarAccessoryContainer?.removeView(currencyView)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initializeCurrencyViews()
|
||||
toolbarAccessoryContainer?.addView(currencyView)
|
||||
binding?.recyclerView?.setBackgroundResource(R.color.content_background)
|
||||
binding?.recyclerView?.onRefresh = {
|
||||
loadShopInventory()
|
||||
}
|
||||
binding?.refreshLayout?.setOnRefreshListener {
|
||||
loadShopInventory()
|
||||
}
|
||||
adapter = binding?.recyclerView?.adapter as? ShopRecyclerAdapter
|
||||
if (adapter == null) {
|
||||
adapter = ShopRecyclerAdapter()
|
||||
adapter?.onNeedsRefresh = {
|
||||
loadShopInventory()
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
adapter?.onShowPurchaseDialog = { item, isPinned ->
|
||||
if (item.key == "gem" && userViewModel.user.value?.isSubscribed != true) {
|
||||
Analytics.sendEvent("View gems for gold CTA", EventCategory.BEHAVIOUR, HitType.EVENT)
|
||||
val subscriptionBottomSheet = EventOutcomeSubscriptionBottomSheetFragment().apply {
|
||||
eventType = EventOutcomeSubscriptionBottomSheetFragment.EVENT_GEMS_FOR_GOLD
|
||||
}
|
||||
activity?.let { activity ->
|
||||
subscriptionBottomSheet.show(activity.supportFragmentManager, SubscriptionBottomSheetFragment.TAG)
|
||||
}
|
||||
} else {
|
||||
val dialog = PurchaseDialog(
|
||||
requireContext(),
|
||||
userRepository,
|
||||
inventoryRepository,
|
||||
item,
|
||||
mainActivity
|
||||
)
|
||||
dialog.shopIdentifier = shopIdentifier
|
||||
dialog.isPinned = isPinned
|
||||
dialog.onShopNeedsRefresh = {
|
||||
loadShopInventory()
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
adapter?.context = context
|
||||
adapter?.mainActivity = mainActivity
|
||||
binding?.recyclerView?.adapter = adapter
|
||||
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
|
||||
adapter?.changeClassEvents = {
|
||||
showClassChangeDialog(it)
|
||||
}
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getInAppReward("armoire").collect {
|
||||
adapter?.armoireItem = it
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getArmoireRemainingCount().collect {
|
||||
adapter?.armoireCount = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (binding?.recyclerView?.layoutManager == null) {
|
||||
layoutManager = GridLayoutManager(context, 2)
|
||||
layoutManager?.spanSizeLookup = object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if ((adapter?.getItemViewType(position) ?: 0) < 4) {
|
||||
layoutManager?.spanCount ?: 1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
binding?.recyclerView?.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
this.shopIdentifier = savedInstanceState.getString(SHOP_IDENTIFIER_KEY, "")
|
||||
}
|
||||
|
||||
adapter?.selectedGearCategory = userViewModel.user.value?.stats?.habitClass ?: ""
|
||||
|
||||
if (shop != null) {
|
||||
adapter?.setShop(shop)
|
||||
}
|
||||
adapter?.shopSpriteSuffix = configManager.shopSpriteSuffix()
|
||||
|
||||
val categories = gearCategories
|
||||
if (categories != null) {
|
||||
adapter?.gearCategories = categories
|
||||
} else {
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
|
||||
userViewModel.user.observe(viewLifecycleOwner) {
|
||||
adapter?.user = it
|
||||
hourglasses.value = it?.hourglassCount?.toDouble() ?: 0.0
|
||||
gems.value = it?.gemCount?.toDouble() ?: 0.0
|
||||
gold.value = it?.stats?.gp ?: 0.0
|
||||
}
|
||||
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
socialRepository.getGroup(Group.TAVERN_ID)
|
||||
.filter { it?.hasActiveQuest == true }
|
||||
.filter { group -> group?.quest?.rageStrikes?.any { it.key == shopIdentifier } ?: false }
|
||||
.filter { group -> group?.quest?.rageStrikes?.filter { it.key == shopIdentifier }?.get(0)?.wasHit == true }
|
||||
.collect {
|
||||
adapter?.shopSpriteSuffix = "_" + it?.quest?.key
|
||||
}
|
||||
}
|
||||
|
||||
view.post { setGridSpanCount(view.width) }
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getOwnedItems()
|
||||
.collect { adapter?.setOwnedItems(it) }
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getInAppRewards()
|
||||
.map { rewards -> rewards.map { it.key } }
|
||||
.collect { adapter?.setPinnedItemKeys(it) }
|
||||
}
|
||||
|
||||
Analytics.sendNavigationEvent("$shopIdentifier screen")
|
||||
}
|
||||
|
||||
open fun initializeCurrencyViews() {
|
||||
currencyView.setContent {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
gems.value?.let { CurrencyText(currency = "gems", value = it) }
|
||||
gold.value?.let { CurrencyText(currency = "gold", value = it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showClassChangeDialog(classIdentifier: String) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val user = userViewModel.user.value ?: return@launch
|
||||
context?.let { context ->
|
||||
if (user.gemCount <= 2) {
|
||||
val dialog = mainActivity?.let { InsufficientGemsDialog(it, 3) }
|
||||
Analytics.sendEvent("show insufficient gems modal", EventCategory.BEHAVIOUR, HitType.EVENT, mapOf("reason" to "class change"))
|
||||
dialog?.show()
|
||||
return@launch
|
||||
}
|
||||
if (user.flags?.classSelected == true && user.preferences?.disableClasses == false) {
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setTitle(getString(R.string.change_class_selected_confirmation, classIdentifier))
|
||||
alert.setMessage(getString(R.string.change_class_equipment_warning))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ ->
|
||||
val dialog = HabiticaProgressDialog.show(
|
||||
requireActivity(),
|
||||
getString(R.string.changing_class_progress),
|
||||
300
|
||||
)
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(classIdentifier)
|
||||
dialog.dismiss()
|
||||
displayClassChanged(classIdentifier)
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.close, false)
|
||||
alert.show()
|
||||
} else {
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setTitle(getString(R.string.class_confirmation, classIdentifier))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ ->
|
||||
val dialog = HabiticaProgressDialog.show(
|
||||
requireActivity(),
|
||||
getString(R.string.changing_class_progress),
|
||||
300
|
||||
)
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(classIdentifier)
|
||||
dialog.dismiss()
|
||||
displayClassChanged(classIdentifier)
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.close, false)
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (shop == null) {
|
||||
loadShopInventory()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadShopInventory() {
|
||||
val shopUrl = when (this.shopIdentifier) {
|
||||
Shop.MARKET -> "market"
|
||||
Shop.QUEST_SHOP -> "quests"
|
||||
Shop.TIME_TRAVELERS_SHOP -> "time-travelers"
|
||||
Shop.SEASONAL_SHOP -> "seasonal"
|
||||
Shop.CUSTOMIZATIONS -> "customizations"
|
||||
else -> ""
|
||||
}
|
||||
lifecycleScope.launchCatching({
|
||||
binding?.recyclerView?.state = RecyclerViewState.FAILED
|
||||
}) {
|
||||
val shop1 = inventoryRepository.retrieveShopInventory(shopUrl) ?: return@launchCatching
|
||||
when (shop1.identifier) {
|
||||
Shop.MARKET -> {
|
||||
val user = userViewModel.user.value
|
||||
val specialCategory = ShopCategory()
|
||||
specialCategory.text = getString(R.string.special)
|
||||
val item = ShopItem.makeGemItem(context?.resources)
|
||||
if (user?.isSubscribed == true) {
|
||||
item.limitedNumberLeft = user.purchased?.plan?.numberOfGemsLeft
|
||||
} else {
|
||||
item.limitedNumberLeft = -1
|
||||
}
|
||||
specialCategory.items.add(item)
|
||||
specialCategory.items.add(ShopItem.makeFortifyItem(context?.resources))
|
||||
shop1.categories.add(specialCategory)
|
||||
}
|
||||
Shop.TIME_TRAVELERS_SHOP -> {
|
||||
formatTimeTravelersShop(shop1)
|
||||
}
|
||||
Shop.SEASONAL_SHOP -> {
|
||||
shop1.categories.sortWith(
|
||||
compareBy<ShopCategory> { it.items.firstOrNull()?.currency != "gold" }
|
||||
.thenByDescending { it.items.firstOrNull()?.event?.end }
|
||||
.thenBy { it.items.firstOrNull()?.locked }
|
||||
)
|
||||
}
|
||||
}
|
||||
shop = shop1
|
||||
adapter?.setShop(shop1)
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatTimeTravelersShop(shop: Shop): Shop {
|
||||
val newCategories = mutableListOf<ShopCategory>()
|
||||
for (category in shop.categories) {
|
||||
if (category.pinType != "mystery_set") {
|
||||
newCategories.add(category)
|
||||
} else {
|
||||
val newCategory = newCategories.find { it.identifier == "mystery_sets" } ?: ShopCategory()
|
||||
if (newCategory.identifier.isEmpty()) {
|
||||
newCategory.identifier = "mystery_sets"
|
||||
newCategory.text = getString(R.string.mystery_sets)
|
||||
newCategories.add(newCategory)
|
||||
}
|
||||
val item = category.items.firstOrNull() ?: continue
|
||||
item.key = category.identifier
|
||||
item.text = category.text
|
||||
item.imageName = "shop_set_mystery_${item.key}"
|
||||
item.pinType = "mystery_set"
|
||||
item.path = "mystery.${item.key}"
|
||||
newCategory.items.add(item)
|
||||
}
|
||||
}
|
||||
shop.categories = newCategories
|
||||
return shop
|
||||
}
|
||||
|
||||
private fun loadMarketGear() {
|
||||
lifecycleScope.launchCatching {
|
||||
val shop = inventoryRepository.retrieveMarketGear()
|
||||
val equipment = inventoryRepository.getOwnedEquipment()
|
||||
.map { equipment -> equipment.map { it.key } }.firstOrNull()
|
||||
for (category in shop?.categories ?: emptyList()) {
|
||||
val items = category.items.asSequence().filter {
|
||||
equipment?.contains(it.key) == false
|
||||
}.sortedBy { it.locked }.toList()
|
||||
category.items.clear()
|
||||
category.items.addAll(items)
|
||||
}
|
||||
gearCategories = shop?.categories
|
||||
adapter?.gearCategories = shop?.categories ?: mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(SHOP_IDENTIFIER_KEY, this.shopIdentifier)
|
||||
}
|
||||
|
||||
private fun setGridSpanCount(width: Int) {
|
||||
var spanCount = 0
|
||||
context?.let { context ->
|
||||
val itemWidth: Float = context.resources.getDimension(R.dimen.reward_width)
|
||||
|
||||
spanCount = (width / itemWidth).toInt()
|
||||
}
|
||||
if (spanCount == 0) {
|
||||
spanCount = 1
|
||||
}
|
||||
layoutManager?.spanCount = spanCount
|
||||
layoutManager?.requestLayout()
|
||||
}
|
||||
|
||||
private fun displayClassChanged(selectedClass: String) {
|
||||
context?.let { context ->
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setMessage(getString(R.string.class_changed_description, selectedClass))
|
||||
alert.addButton(getString(R.string.complete_tutorial), true) { _, _ -> alert.dismiss() }
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SHOP_IDENTIFIER_KEY = "SHOP_IDENTIFIER_KEY"
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.fragments.inventory.shops
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
import com.habitrpg.android.habitica.data.SocialRepository
|
||||
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
|
||||
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.models.shops.Shop
|
||||
import com.habitrpg.android.habitica.models.shops.ShopCategory
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import com.habitrpg.android.habitica.ui.adapter.inventory.ShopRecyclerAdapter
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.purchases.EventOutcomeSubscriptionBottomSheetFragment
|
||||
import com.habitrpg.android.habitica.ui.fragments.purchases.SubscriptionBottomSheetFragment
|
||||
import com.habitrpg.android.habitica.ui.helpers.SafeDefaultItemAnimator
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.CurrencyText
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaProgressDialog
|
||||
import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGemsDialog
|
||||
import com.habitrpg.android.habitica.ui.views.shops.PurchaseDialog
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.common.habitica.helpers.RecyclerViewState
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
open class ShopFragment : BaseMainFragment<FragmentRefreshRecyclerviewBinding>() {
|
||||
internal val currencyView: ComposeView by lazy {
|
||||
return@lazy ComposeView(requireContext())
|
||||
}
|
||||
|
||||
var adapter: ShopRecyclerAdapter? = null
|
||||
var shopIdentifier: String? = null
|
||||
var shop: Shop? = null
|
||||
internal val hourglasses = mutableStateOf<Double?>(null)
|
||||
private val gems = mutableStateOf<Double?>(null)
|
||||
private val gold = mutableStateOf<Double?>(null)
|
||||
|
||||
@Inject
|
||||
lateinit var inventoryRepository: InventoryRepository
|
||||
|
||||
@Inject
|
||||
lateinit var socialRepository: SocialRepository
|
||||
|
||||
@Inject
|
||||
lateinit var configManager: AppConfigManager
|
||||
|
||||
@Inject
|
||||
lateinit var userViewModel: MainUserViewModel
|
||||
|
||||
private var layoutManager: GridLayoutManager? = null
|
||||
|
||||
private var gearCategories: MutableList<ShopCategory>? = null
|
||||
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
override fun createBinding(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
): FragmentRefreshRecyclerviewBinding {
|
||||
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View? {
|
||||
this.hidesToolbar = true
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
inventoryRepository.close()
|
||||
socialRepository.close()
|
||||
toolbarAccessoryContainer?.removeView(currencyView)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
override fun onViewCreated(
|
||||
view: View,
|
||||
savedInstanceState: Bundle?,
|
||||
) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initializeCurrencyViews()
|
||||
toolbarAccessoryContainer?.addView(currencyView)
|
||||
binding?.recyclerView?.setBackgroundResource(R.color.content_background)
|
||||
binding?.recyclerView?.onRefresh = {
|
||||
loadShopInventory()
|
||||
}
|
||||
binding?.refreshLayout?.setOnRefreshListener {
|
||||
loadShopInventory()
|
||||
}
|
||||
adapter = binding?.recyclerView?.adapter as? ShopRecyclerAdapter
|
||||
if (adapter == null) {
|
||||
adapter = ShopRecyclerAdapter()
|
||||
adapter?.onNeedsRefresh = {
|
||||
loadShopInventory()
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
adapter?.onShowPurchaseDialog = { item, isPinned ->
|
||||
if (item.key == "gem" && userViewModel.user.value?.isSubscribed != true) {
|
||||
Analytics.sendEvent("View gems for gold CTA", EventCategory.BEHAVIOUR, HitType.EVENT)
|
||||
val subscriptionBottomSheet =
|
||||
EventOutcomeSubscriptionBottomSheetFragment().apply {
|
||||
eventType = EventOutcomeSubscriptionBottomSheetFragment.EVENT_GEMS_FOR_GOLD
|
||||
}
|
||||
activity?.let { activity ->
|
||||
subscriptionBottomSheet.show(activity.supportFragmentManager, SubscriptionBottomSheetFragment.TAG)
|
||||
}
|
||||
} else {
|
||||
val dialog =
|
||||
PurchaseDialog(
|
||||
requireContext(),
|
||||
userRepository,
|
||||
inventoryRepository,
|
||||
item,
|
||||
mainActivity,
|
||||
)
|
||||
dialog.shopIdentifier = shopIdentifier
|
||||
dialog.isPinned = isPinned
|
||||
dialog.onShopNeedsRefresh = {
|
||||
loadShopInventory()
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
dialog.show()
|
||||
}
|
||||
}
|
||||
|
||||
adapter?.context = context
|
||||
adapter?.mainActivity = mainActivity
|
||||
binding?.recyclerView?.adapter = adapter
|
||||
binding?.recyclerView?.itemAnimator = SafeDefaultItemAnimator()
|
||||
adapter?.changeClassEvents = {
|
||||
showClassChangeDialog(it)
|
||||
}
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getInAppReward("armoire").collect {
|
||||
adapter?.armoireItem = it
|
||||
}
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getArmoireRemainingCount().collect {
|
||||
adapter?.armoireCount = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (binding?.recyclerView?.layoutManager == null) {
|
||||
layoutManager = GridLayoutManager(context, 2)
|
||||
layoutManager?.spanSizeLookup =
|
||||
object : GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if ((adapter?.getItemViewType(position) ?: 0) < 4) {
|
||||
layoutManager?.spanCount ?: 1
|
||||
} else {
|
||||
1
|
||||
}
|
||||
}
|
||||
}
|
||||
binding?.recyclerView?.layoutManager = layoutManager
|
||||
}
|
||||
|
||||
if (savedInstanceState != null) {
|
||||
this.shopIdentifier = savedInstanceState.getString(SHOP_IDENTIFIER_KEY, "")
|
||||
}
|
||||
|
||||
adapter?.selectedGearCategory = userViewModel.user.value?.stats?.habitClass ?: ""
|
||||
|
||||
if (shop != null) {
|
||||
adapter?.setShop(shop)
|
||||
}
|
||||
adapter?.shopSpriteSuffix = configManager.shopSpriteSuffix()
|
||||
|
||||
val categories = gearCategories
|
||||
if (categories != null) {
|
||||
adapter?.gearCategories = categories
|
||||
} else {
|
||||
if (Shop.MARKET == shopIdentifier) {
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
|
||||
userViewModel.user.observe(viewLifecycleOwner) {
|
||||
adapter?.user = it
|
||||
hourglasses.value = it?.hourglassCount?.toDouble() ?: 0.0
|
||||
gems.value = it?.gemCount?.toDouble() ?: 0.0
|
||||
gold.value = it?.stats?.gp ?: 0.0
|
||||
}
|
||||
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
socialRepository.getGroup(Group.TAVERN_ID)
|
||||
.filter { it?.hasActiveQuest == true }
|
||||
.filter { group -> group?.quest?.rageStrikes?.any { it.key == shopIdentifier } ?: false }
|
||||
.filter { group -> group?.quest?.rageStrikes?.filter { it.key == shopIdentifier }?.get(0)?.wasHit == true }
|
||||
.collect {
|
||||
adapter?.shopSpriteSuffix = "_" + it?.quest?.key
|
||||
}
|
||||
}
|
||||
|
||||
view.post { setGridSpanCount(view.width) }
|
||||
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getOwnedItems()
|
||||
.collect { adapter?.setOwnedItems(it) }
|
||||
}
|
||||
lifecycleScope.launchCatching {
|
||||
inventoryRepository.getInAppRewards()
|
||||
.map { rewards -> rewards.map { it.key } }
|
||||
.collect { adapter?.setPinnedItemKeys(it) }
|
||||
}
|
||||
|
||||
Analytics.sendNavigationEvent("$shopIdentifier screen")
|
||||
}
|
||||
|
||||
open fun initializeCurrencyViews() {
|
||||
currencyView.setContent {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
gems.value?.let { CurrencyText(currency = "gems", value = it) }
|
||||
gold.value?.let { CurrencyText(currency = "gold", value = it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showClassChangeDialog(classIdentifier: String) {
|
||||
lifecycleScope.launch(ExceptionHandler.coroutine()) {
|
||||
val user = userViewModel.user.value ?: return@launch
|
||||
context?.let { context ->
|
||||
if (user.gemCount <= 2) {
|
||||
val dialog = mainActivity?.let { InsufficientGemsDialog(it, 3) }
|
||||
Analytics.sendEvent("show insufficient gems modal", EventCategory.BEHAVIOUR, HitType.EVENT, mapOf("reason" to "class change"))
|
||||
dialog?.show()
|
||||
return@launch
|
||||
}
|
||||
if (user.flags?.classSelected == true && user.preferences?.disableClasses == false) {
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setTitle(getString(R.string.change_class_selected_confirmation, classIdentifier))
|
||||
alert.setMessage(getString(R.string.change_class_equipment_warning))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ ->
|
||||
val dialog =
|
||||
HabiticaProgressDialog.show(
|
||||
requireActivity(),
|
||||
getString(R.string.changing_class_progress),
|
||||
300,
|
||||
)
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(classIdentifier)
|
||||
dialog.dismiss()
|
||||
displayClassChanged(classIdentifier)
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.close, false)
|
||||
alert.show()
|
||||
} else {
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setTitle(getString(R.string.class_confirmation, classIdentifier))
|
||||
alert.addButton(R.string.choose_class, true) { _, _ ->
|
||||
val dialog =
|
||||
HabiticaProgressDialog.show(
|
||||
requireActivity(),
|
||||
getString(R.string.changing_class_progress),
|
||||
300,
|
||||
)
|
||||
lifecycleScope.launch(Dispatchers.Main) {
|
||||
userRepository.changeClass(classIdentifier)
|
||||
dialog.dismiss()
|
||||
displayClassChanged(classIdentifier)
|
||||
loadMarketGear()
|
||||
}
|
||||
}
|
||||
alert.addButton(R.string.close, false)
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
if (shop == null) {
|
||||
loadShopInventory()
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadShopInventory() {
|
||||
val shopUrl =
|
||||
when (this.shopIdentifier) {
|
||||
Shop.MARKET -> "market"
|
||||
Shop.QUEST_SHOP -> "quests"
|
||||
Shop.TIME_TRAVELERS_SHOP -> "time-travelers"
|
||||
Shop.SEASONAL_SHOP -> "seasonal"
|
||||
Shop.CUSTOMIZATIONS -> "customizations"
|
||||
else -> ""
|
||||
}
|
||||
lifecycleScope.launchCatching({
|
||||
binding?.recyclerView?.state = RecyclerViewState.FAILED
|
||||
}) {
|
||||
val shop1 = inventoryRepository.retrieveShopInventory(shopUrl) ?: return@launchCatching
|
||||
when (shop1.identifier) {
|
||||
Shop.MARKET -> {
|
||||
val user = userViewModel.user.value
|
||||
val specialCategory = ShopCategory()
|
||||
specialCategory.text = getString(R.string.special)
|
||||
val item = ShopItem.makeGemItem(context?.resources)
|
||||
if (user?.isSubscribed == true) {
|
||||
item.limitedNumberLeft = user.purchased?.plan?.numberOfGemsLeft
|
||||
} else {
|
||||
item.limitedNumberLeft = -1
|
||||
}
|
||||
specialCategory.items.add(item)
|
||||
specialCategory.items.add(ShopItem.makeFortifyItem(context?.resources))
|
||||
shop1.categories.add(specialCategory)
|
||||
}
|
||||
Shop.TIME_TRAVELERS_SHOP -> {
|
||||
formatTimeTravelersShop(shop1)
|
||||
}
|
||||
Shop.SEASONAL_SHOP -> {
|
||||
shop1.categories.sortWith(
|
||||
compareBy<ShopCategory> { it.items.firstOrNull()?.currency != "gold" }
|
||||
.thenByDescending { it.items.firstOrNull()?.event?.end }
|
||||
.thenBy { it.items.firstOrNull()?.locked },
|
||||
)
|
||||
}
|
||||
}
|
||||
shop = shop1
|
||||
adapter?.setShop(shop1)
|
||||
binding?.refreshLayout?.isRefreshing = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun formatTimeTravelersShop(shop: Shop): Shop {
|
||||
val newCategories = mutableListOf<ShopCategory>()
|
||||
for (category in shop.categories) {
|
||||
if (category.pinType != "mystery_set") {
|
||||
newCategories.add(category)
|
||||
} else {
|
||||
val newCategory = newCategories.find { it.identifier == "mystery_sets" } ?: ShopCategory()
|
||||
if (newCategory.identifier.isEmpty()) {
|
||||
newCategory.identifier = "mystery_sets"
|
||||
newCategory.text = getString(R.string.mystery_sets)
|
||||
newCategories.add(newCategory)
|
||||
}
|
||||
val item = category.items.firstOrNull() ?: continue
|
||||
item.key = category.identifier
|
||||
item.text = category.text
|
||||
item.imageName = "shop_set_mystery_${item.key}"
|
||||
item.pinType = "mystery_set"
|
||||
item.path = "mystery.${item.key}"
|
||||
newCategory.items.add(item)
|
||||
}
|
||||
}
|
||||
shop.categories = newCategories
|
||||
return shop
|
||||
}
|
||||
|
||||
private fun loadMarketGear() {
|
||||
lifecycleScope.launchCatching {
|
||||
val shop = inventoryRepository.retrieveMarketGear()
|
||||
val equipment =
|
||||
inventoryRepository.getOwnedEquipment()
|
||||
.map { equipment -> equipment.map { it.key } }.firstOrNull()
|
||||
for (category in shop?.categories ?: emptyList()) {
|
||||
val items =
|
||||
category.items.asSequence().filter {
|
||||
equipment?.contains(it.key) == false
|
||||
}.sortedBy { it.locked }.toList()
|
||||
category.items.clear()
|
||||
category.items.addAll(items)
|
||||
}
|
||||
gearCategories = shop?.categories
|
||||
adapter?.gearCategories = shop?.categories ?: mutableListOf()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(SHOP_IDENTIFIER_KEY, this.shopIdentifier)
|
||||
}
|
||||
|
||||
private fun setGridSpanCount(width: Int) {
|
||||
var spanCount = 0
|
||||
context?.let { context ->
|
||||
val itemWidth: Float = context.resources.getDimension(R.dimen.reward_width)
|
||||
|
||||
spanCount = (width / itemWidth).toInt()
|
||||
}
|
||||
if (spanCount == 0) {
|
||||
spanCount = 1
|
||||
}
|
||||
layoutManager?.spanCount = spanCount
|
||||
layoutManager?.requestLayout()
|
||||
}
|
||||
|
||||
private fun displayClassChanged(selectedClass: String) {
|
||||
context?.let { context ->
|
||||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setMessage(getString(R.string.class_changed_description, selectedClass))
|
||||
alert.addButton(getString(R.string.complete_tutorial), true) { _, _ -> alert.dismiss() }
|
||||
alert.show()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val SHOP_IDENTIFIER_KEY = "SHOP_IDENTIFIER_KEY"
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,305 +1,319 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.BindableViewHolder
|
||||
import com.habitrpg.android.habitica.ui.views.EllipsisTextView
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import com.habitrpg.common.habitica.helpers.setParsedMarkdown
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
abstract class BaseTaskViewHolder(
|
||||
itemView: View,
|
||||
var scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
var openTaskFunc: ((Task, View) -> Unit),
|
||||
var brokenTaskFunc: ((Task) -> Unit),
|
||||
var assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : BindableViewHolder<Task>(itemView), View.OnTouchListener {
|
||||
private val scope = MainScope()
|
||||
|
||||
var task: Task? = null
|
||||
var movingFromPosition: Int? = null
|
||||
var errorButtonClicked: (() -> Unit)? = null
|
||||
var userID: String? = null
|
||||
var isLocked = false
|
||||
protected var context: Context
|
||||
private val mainTaskWrapper: ViewGroup = itemView.findViewById(R.id.main_task_wrapper)
|
||||
protected val assignedTextView: TextView = itemView.findViewById(R.id.assigned_textview)
|
||||
protected val completedCountTextView: TextView = itemView.findViewById(R.id.completed_textview)
|
||||
protected val titleTextView: EllipsisTextView = itemView.findViewById(R.id.checkedTextView)
|
||||
protected val notesTextView: EllipsisTextView? = itemView.findViewById(R.id.notesTextView)
|
||||
protected val calendarIconView: ImageView? = itemView.findViewById(R.id.iconViewCalendar)
|
||||
protected val iconViewTeam: ImageView? = itemView.findViewById(R.id.iconViewTeamTask)
|
||||
protected val specialTaskTextView: TextView? = itemView.findViewById(R.id.specialTaskText)
|
||||
private val iconViewChallenge: ImageView? = itemView.findViewById(R.id.iconviewChallenge)
|
||||
private val iconViewReminder: ImageView? = itemView.findViewById(R.id.iconviewReminder)
|
||||
private val taskIconWrapper: LinearLayout? = itemView.findViewById(R.id.taskIconWrapper)
|
||||
private val approvalRequiredTextView: TextView =
|
||||
itemView.findViewById(R.id.approvalRequiredTextField)
|
||||
private val expandNotesButton: Button? = itemView.findViewById(R.id.expand_notes_button)
|
||||
private val syncingView: ProgressBar? = itemView.findViewById(R.id.syncing_view)
|
||||
private val errorIconView: ImageButton? = itemView.findViewById(R.id.error_icon)
|
||||
protected val taskGray: Int =
|
||||
ContextCompat.getColor(itemView.context, R.color.offset_background)
|
||||
protected val streakIconView: ImageView = itemView.findViewById(R.id.iconViewStreak)
|
||||
protected val streakTextView: TextView = itemView.findViewById(R.id.streakTextView)
|
||||
protected val reminderTextView: TextView = itemView.findViewById(R.id.reminder_textview)
|
||||
|
||||
private var openTaskDisabled: Boolean = false
|
||||
private var taskActionsDisabled: Boolean = false
|
||||
private var notesExpanded = false
|
||||
|
||||
protected open val taskIconWrapperIsVisible: Boolean
|
||||
get() {
|
||||
var isVisible = false
|
||||
|
||||
if (iconViewTeam?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewReminder?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewChallenge?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewReminder?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (specialTaskTextView?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (this.streakTextView.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
return isVisible
|
||||
}
|
||||
|
||||
init {
|
||||
itemView.setOnTouchListener(this)
|
||||
itemView.isClickable = true
|
||||
mainTaskWrapper.clipToOutline = true
|
||||
|
||||
titleTextView.setOnClickListener { onTouch(it, null) }
|
||||
notesTextView?.setOnClickListener { onTouch(it, null) }
|
||||
errorIconView?.setOnClickListener { errorButtonClicked?.invoke() }
|
||||
|
||||
notesTextView?.movementMethod = LinkMovementMethod.getInstance()
|
||||
titleTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
expandNotesButton?.setOnClickListener { expandTask() }
|
||||
iconViewChallenge?.setOnClickListener {
|
||||
task?.let { t ->
|
||||
if (task?.challengeBroken?.isNotBlank() == true) brokenTaskFunc(t)
|
||||
}
|
||||
}
|
||||
notesTextView?.addEllipsesListener(object : EllipsisTextView.EllipsisListener {
|
||||
override fun ellipsisStateChanged(ellipses: Boolean) {
|
||||
scope.launch(Dispatchers.Main.immediate) {
|
||||
if (ellipses && notesTextView.maxLines != 3) {
|
||||
notesTextView.maxLines = 3
|
||||
}
|
||||
expandNotesButton?.visibility =
|
||||
if (ellipses || notesExpanded) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
})
|
||||
context = itemView.context
|
||||
}
|
||||
|
||||
private fun expandTask() {
|
||||
notesExpanded = !notesExpanded
|
||||
if (notesExpanded) {
|
||||
notesTextView?.maxLines = 100
|
||||
expandNotesButton?.text = context.getString(R.string.collapse_notes)
|
||||
} else {
|
||||
notesTextView?.maxLines = 8
|
||||
expandNotesButton?.text = context.getString(R.string.expand_notes)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(data: Task, position: Int, displayMode: String) {
|
||||
bind(data, position, displayMode, null)
|
||||
}
|
||||
|
||||
open fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?
|
||||
) {
|
||||
notesExpanded = false
|
||||
task = data
|
||||
itemView.setBackgroundColor(context.getThemeColor(R.attr.colorContentBackground))
|
||||
|
||||
expandNotesButton?.visibility = View.GONE
|
||||
notesExpanded = false
|
||||
notesTextView?.maxLines = 8
|
||||
if (data.notes?.isNotEmpty() == true) {
|
||||
notesTextView?.visibility = View.VISIBLE
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_ternary))
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
titleTextView.text = data.text
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (data.text.isNotEmpty() && MarkdownParser.containsMarkdown(data.text)) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(data.text)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedText = parsedText
|
||||
titleTextView.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (displayMode != "minimal") {
|
||||
notesTextView?.text = data.notes
|
||||
data.notes?.let { notes ->
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (notes.isEmpty() || !MarkdownParser.containsMarkdown(notes)) {
|
||||
return@launch
|
||||
}
|
||||
val parsedNotes = MarkdownParser.parseMarkdown(notes)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedNotes = parsedNotes
|
||||
notesTextView?.setParsedMarkdown(parsedNotes)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
|
||||
if (displayMode == "standard") {
|
||||
iconViewReminder?.visibility =
|
||||
if ((data.reminders?.size ?: 0) > 0) View.VISIBLE else View.GONE
|
||||
|
||||
iconViewChallenge?.visibility =
|
||||
if (task?.challengeID != null) View.VISIBLE else View.GONE
|
||||
if (task?.challengeID != null) {
|
||||
if (task?.challengeBroken?.isNotBlank() == true) {
|
||||
iconViewChallenge?.alpha = 1.0f
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.white)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_broken_megaphone)
|
||||
} else {
|
||||
iconViewChallenge?.alpha = 0.3f
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.text_ternary)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_megaphone)
|
||||
}
|
||||
}
|
||||
configureSpecialTaskTextView(data)
|
||||
|
||||
iconViewTeam?.visibility =
|
||||
if (data.isGroupTask && ownerID != data.group?.groupID) View.VISIBLE else View.GONE
|
||||
|
||||
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
taskIconWrapper?.visibility = View.GONE
|
||||
mainTaskWrapper.minimumHeight = 48.dpToPx(context)
|
||||
}
|
||||
|
||||
if (data.isPendingApproval) {
|
||||
approvalRequiredTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
approvalRequiredTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (data.group?.assignedUsers?.isNotEmpty() == true) {
|
||||
assignedTextView.text = assignedTextProvider?.assignedTextForTask(
|
||||
context.resources,
|
||||
data.group?.assignedUsers ?: emptyList()
|
||||
)
|
||||
assignedTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
assignedTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
val completedCount = data.group?.assignedUsersDetail?.filter { it.completed }?.size ?: 0
|
||||
if (completedCount > 0) {
|
||||
completedCountTextView.text =
|
||||
"$completedCount/${data.group?.assignedUsersDetail?.size}"
|
||||
completedCountTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
completedCountTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
syncingView?.visibility = if (task?.isSaving == true) View.VISIBLE else View.GONE
|
||||
errorIconView?.visibility = if (task?.hasErrored == true) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
protected open fun configureSpecialTaskTextView(task: Task) {
|
||||
specialTaskTextView?.visibility = View.INVISIBLE
|
||||
calendarIconView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
open fun onLeftActionTouched() {}
|
||||
open fun onRightActionTouched() {}
|
||||
|
||||
override fun onTouch(view: View?, motionEvent: MotionEvent?): Boolean {
|
||||
if (motionEvent != null) {
|
||||
if (motionEvent.action != MotionEvent.ACTION_UP) return true
|
||||
if (motionEvent.y <= mainTaskWrapper.height + 5.dpToPx(context)) {
|
||||
if ((this.task?.checklist?.isNotEmpty() != true)) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder)
|
||||
if (mainTaskWrapper.height == checkboxHolder.height) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if (motionEvent.y <= (checkboxHolder.height + 5.dpToPx(context))) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
task?.let {
|
||||
openTaskFunc(it, mainTaskWrapper)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
open fun setDisabled(openTaskDisabled: Boolean, taskActionsDisabled: Boolean) {
|
||||
this.openTaskDisabled = openTaskDisabled
|
||||
this.taskActionsDisabled = taskActionsDisabled
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.text.method.LinkMovementMethod
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.Button
|
||||
import android.widget.ImageButton
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.ProgressBar
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.ui.viewHolders.BindableViewHolder
|
||||
import com.habitrpg.android.habitica.ui.views.EllipsisTextView
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import com.habitrpg.common.habitica.helpers.setParsedMarkdown
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
abstract class BaseTaskViewHolder(
|
||||
itemView: View,
|
||||
var scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
var openTaskFunc: ((Task, View) -> Unit),
|
||||
var brokenTaskFunc: ((Task) -> Unit),
|
||||
var assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : BindableViewHolder<Task>(itemView), View.OnTouchListener {
|
||||
private val scope = MainScope()
|
||||
|
||||
var task: Task? = null
|
||||
var movingFromPosition: Int? = null
|
||||
var errorButtonClicked: (() -> Unit)? = null
|
||||
var userID: String? = null
|
||||
var isLocked = false
|
||||
protected var context: Context
|
||||
private val mainTaskWrapper: ViewGroup = itemView.findViewById(R.id.main_task_wrapper)
|
||||
protected val assignedTextView: TextView = itemView.findViewById(R.id.assigned_textview)
|
||||
protected val completedCountTextView: TextView = itemView.findViewById(R.id.completed_textview)
|
||||
protected val titleTextView: EllipsisTextView = itemView.findViewById(R.id.checkedTextView)
|
||||
protected val notesTextView: EllipsisTextView? = itemView.findViewById(R.id.notesTextView)
|
||||
protected val calendarIconView: ImageView? = itemView.findViewById(R.id.iconViewCalendar)
|
||||
protected val iconViewTeam: ImageView? = itemView.findViewById(R.id.iconViewTeamTask)
|
||||
protected val specialTaskTextView: TextView? = itemView.findViewById(R.id.specialTaskText)
|
||||
private val iconViewChallenge: ImageView? = itemView.findViewById(R.id.iconviewChallenge)
|
||||
private val iconViewReminder: ImageView? = itemView.findViewById(R.id.iconviewReminder)
|
||||
private val taskIconWrapper: LinearLayout? = itemView.findViewById(R.id.taskIconWrapper)
|
||||
private val approvalRequiredTextView: TextView =
|
||||
itemView.findViewById(R.id.approvalRequiredTextField)
|
||||
private val expandNotesButton: Button? = itemView.findViewById(R.id.expand_notes_button)
|
||||
private val syncingView: ProgressBar? = itemView.findViewById(R.id.syncing_view)
|
||||
private val errorIconView: ImageButton? = itemView.findViewById(R.id.error_icon)
|
||||
protected val taskGray: Int =
|
||||
ContextCompat.getColor(itemView.context, R.color.offset_background)
|
||||
protected val streakIconView: ImageView = itemView.findViewById(R.id.iconViewStreak)
|
||||
protected val streakTextView: TextView = itemView.findViewById(R.id.streakTextView)
|
||||
protected val reminderTextView: TextView = itemView.findViewById(R.id.reminder_textview)
|
||||
|
||||
private var openTaskDisabled: Boolean = false
|
||||
private var taskActionsDisabled: Boolean = false
|
||||
private var notesExpanded = false
|
||||
|
||||
protected open val taskIconWrapperIsVisible: Boolean
|
||||
get() {
|
||||
var isVisible = false
|
||||
|
||||
if (iconViewTeam?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewReminder?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewChallenge?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (iconViewReminder?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (specialTaskTextView?.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
if (this.streakTextView.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
return isVisible
|
||||
}
|
||||
|
||||
init {
|
||||
itemView.setOnTouchListener(this)
|
||||
itemView.isClickable = true
|
||||
mainTaskWrapper.clipToOutline = true
|
||||
|
||||
titleTextView.setOnClickListener { onTouch(it, null) }
|
||||
notesTextView?.setOnClickListener { onTouch(it, null) }
|
||||
errorIconView?.setOnClickListener { errorButtonClicked?.invoke() }
|
||||
|
||||
notesTextView?.movementMethod = LinkMovementMethod.getInstance()
|
||||
titleTextView.movementMethod = LinkMovementMethod.getInstance()
|
||||
|
||||
expandNotesButton?.setOnClickListener { expandTask() }
|
||||
iconViewChallenge?.setOnClickListener {
|
||||
task?.let { t ->
|
||||
if (task?.challengeBroken?.isNotBlank() == true) brokenTaskFunc(t)
|
||||
}
|
||||
}
|
||||
notesTextView?.addEllipsesListener(
|
||||
object : EllipsisTextView.EllipsisListener {
|
||||
override fun ellipsisStateChanged(ellipses: Boolean) {
|
||||
scope.launch(Dispatchers.Main.immediate) {
|
||||
if (ellipses && notesTextView.maxLines != 3) {
|
||||
notesTextView.maxLines = 3
|
||||
}
|
||||
expandNotesButton?.visibility =
|
||||
if (ellipses || notesExpanded) View.VISIBLE else View.GONE
|
||||
}
|
||||
}
|
||||
},
|
||||
)
|
||||
context = itemView.context
|
||||
}
|
||||
|
||||
private fun expandTask() {
|
||||
notesExpanded = !notesExpanded
|
||||
if (notesExpanded) {
|
||||
notesTextView?.maxLines = 100
|
||||
expandNotesButton?.text = context.getString(R.string.collapse_notes)
|
||||
} else {
|
||||
notesTextView?.maxLines = 8
|
||||
expandNotesButton?.text = context.getString(R.string.expand_notes)
|
||||
}
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
) {
|
||||
bind(data, position, displayMode, null)
|
||||
}
|
||||
|
||||
open fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
notesExpanded = false
|
||||
task = data
|
||||
itemView.setBackgroundColor(context.getThemeColor(R.attr.colorContentBackground))
|
||||
|
||||
expandNotesButton?.visibility = View.GONE
|
||||
notesExpanded = false
|
||||
notesTextView?.maxLines = 8
|
||||
if (data.notes?.isNotEmpty() == true) {
|
||||
notesTextView?.visibility = View.VISIBLE
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_ternary))
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
titleTextView.text = data.text
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (data.text.isNotEmpty() && MarkdownParser.containsMarkdown(data.text)) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(data.text)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedText = parsedText
|
||||
titleTextView.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (displayMode != "minimal") {
|
||||
notesTextView?.text = data.notes
|
||||
data.notes?.let { notes ->
|
||||
scope.launch(Dispatchers.IO) {
|
||||
if (notes.isEmpty() || !MarkdownParser.containsMarkdown(notes)) {
|
||||
return@launch
|
||||
}
|
||||
val parsedNotes = MarkdownParser.parseMarkdown(notes)
|
||||
withContext(Dispatchers.Main) {
|
||||
data.parsedNotes = parsedNotes
|
||||
notesTextView?.setParsedMarkdown(parsedNotes)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
notesTextView?.visibility = View.GONE
|
||||
}
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
|
||||
|
||||
if (displayMode == "standard") {
|
||||
iconViewReminder?.visibility =
|
||||
if ((data.reminders?.size ?: 0) > 0) View.VISIBLE else View.GONE
|
||||
|
||||
iconViewChallenge?.visibility =
|
||||
if (task?.challengeID != null) View.VISIBLE else View.GONE
|
||||
if (task?.challengeID != null) {
|
||||
if (task?.challengeBroken?.isNotBlank() == true) {
|
||||
iconViewChallenge?.alpha = 1.0f
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.white)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_broken_megaphone)
|
||||
} else {
|
||||
iconViewChallenge?.alpha = 0.3f
|
||||
iconViewChallenge?.imageTintList =
|
||||
ContextCompat.getColorStateList(context, R.color.text_ternary)
|
||||
iconViewChallenge?.setImageResource(R.drawable.task_megaphone)
|
||||
}
|
||||
}
|
||||
configureSpecialTaskTextView(data)
|
||||
|
||||
iconViewTeam?.visibility =
|
||||
if (data.isGroupTask && ownerID != data.group?.groupID) View.VISIBLE else View.GONE
|
||||
|
||||
taskIconWrapper?.visibility = if (taskIconWrapperIsVisible) View.VISIBLE else View.GONE
|
||||
} else {
|
||||
taskIconWrapper?.visibility = View.GONE
|
||||
mainTaskWrapper.minimumHeight = 48.dpToPx(context)
|
||||
}
|
||||
|
||||
if (data.isPendingApproval) {
|
||||
approvalRequiredTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
approvalRequiredTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
if (data.group?.assignedUsers?.isNotEmpty() == true) {
|
||||
assignedTextView.text =
|
||||
assignedTextProvider?.assignedTextForTask(
|
||||
context.resources,
|
||||
data.group?.assignedUsers ?: emptyList(),
|
||||
)
|
||||
assignedTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
assignedTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
val completedCount = data.group?.assignedUsersDetail?.filter { it.completed }?.size ?: 0
|
||||
if (completedCount > 0) {
|
||||
completedCountTextView.text =
|
||||
"$completedCount/${data.group?.assignedUsersDetail?.size}"
|
||||
completedCountTextView.visibility = View.VISIBLE
|
||||
} else {
|
||||
completedCountTextView.visibility = View.GONE
|
||||
}
|
||||
|
||||
syncingView?.visibility = if (task?.isSaving == true) View.VISIBLE else View.GONE
|
||||
errorIconView?.visibility = if (task?.hasErrored == true) View.VISIBLE else View.GONE
|
||||
}
|
||||
|
||||
protected open fun configureSpecialTaskTextView(task: Task) {
|
||||
specialTaskTextView?.visibility = View.INVISIBLE
|
||||
calendarIconView?.visibility = View.GONE
|
||||
}
|
||||
|
||||
open fun onLeftActionTouched() {}
|
||||
|
||||
open fun onRightActionTouched() {}
|
||||
|
||||
override fun onTouch(
|
||||
view: View?,
|
||||
motionEvent: MotionEvent?,
|
||||
): Boolean {
|
||||
if (motionEvent != null) {
|
||||
if (motionEvent.action != MotionEvent.ACTION_UP) return true
|
||||
if (motionEvent.y <= mainTaskWrapper.height + 5.dpToPx(context)) {
|
||||
if ((this.task?.checklist?.isNotEmpty() != true)) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder)
|
||||
if (mainTaskWrapper.height == checkboxHolder.height) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if (motionEvent.y <= (checkboxHolder.height + 5.dpToPx(context))) {
|
||||
if (motionEvent.x <= 72.dpToPx(context)) {
|
||||
onLeftActionTouched()
|
||||
return true
|
||||
} else if ((itemView.width - motionEvent.x <= 72.dpToPx(context))) {
|
||||
onRightActionTouched()
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
task?.let {
|
||||
openTaskFunc(it, mainTaskWrapper)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
open fun setDisabled(
|
||||
openTaskDisabled: Boolean,
|
||||
taskActionsDisabled: Boolean,
|
||||
) {
|
||||
this.openTaskDisabled = openTaskDisabled
|
||||
this.taskActionsDisabled = taskActionsDisabled
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,240 +1,247 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
|
||||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import com.habitrpg.common.habitica.helpers.setParsedMarkdown
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
abstract class ChecklistedViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
|
||||
private val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder)
|
||||
private val checkmarkView: ImageView = itemView.findViewById(R.id.checkmark)
|
||||
private val lockView: ImageView = itemView.findViewById(R.id.lock_view)
|
||||
private val checkboxBackground: View = itemView.findViewById(R.id.checkBoxBackground)
|
||||
private val checklistView: LinearLayout = itemView.findViewById(R.id.checklistView)
|
||||
private val checklistIndicatorWrapper: ViewGroup = itemView.findViewById(R.id.checklistIndicatorWrapper)
|
||||
private val checklistCompletedTextView: TextView = itemView.findViewById(R.id.checkListCompletedTextView)
|
||||
private val checklistAllTextView: TextView = itemView.findViewById(R.id.checkListAllTextView)
|
||||
private val checklistDivider: View = itemView.findViewById(R.id.checklistDivider)
|
||||
|
||||
init {
|
||||
checklistIndicatorWrapper.isClickable = true
|
||||
checklistIndicatorWrapper.setOnClickListener { onChecklistIndicatorClicked() }
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?
|
||||
) {
|
||||
var completed = data.completed(userID)
|
||||
if (data.isPendingApproval) {
|
||||
completed = false
|
||||
}
|
||||
if (isLocked) {
|
||||
this.checkmarkView.visibility = View.GONE
|
||||
this.lockView.visibility = View.VISIBLE
|
||||
val icon = AppCompatResources.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, if (data.isDue == true || data.type == TaskType.TODO) data.extraExtraDarkTaskColor else R.color.text_dimmed))
|
||||
lockView.setImageDrawable(icon)
|
||||
} else {
|
||||
this.checkmarkView.visibility = if (completed) View.VISIBLE else View.GONE
|
||||
checkmarkView.drawable.setTint(ContextCompat.getColor(context, R.color.gray_400))
|
||||
this.lockView.visibility = View.GONE
|
||||
}
|
||||
this.checklistCompletedTextView.text = data.completedChecklistCount.toString()
|
||||
this.checklistAllTextView.text = data.checklist?.size.toString()
|
||||
|
||||
this.checklistView.removeAllViews()
|
||||
this.updateChecklistDisplay()
|
||||
|
||||
this.checklistIndicatorWrapper.visibility = if (data.checklist?.size == 0) View.GONE else View.VISIBLE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
val regularBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_unchecked else R.drawable.todo_unchecked
|
||||
val completedBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_checked else R.drawable.todo_checked
|
||||
val inactiveBoxBackground = R.drawable.daily_inactive
|
||||
if (this.shouldDisplayAsActive(data, userID) && !data.isPendingApproval) {
|
||||
this.checkboxHolder.setBackgroundResource(data.lightTaskColor)
|
||||
checkboxBackground.setBackgroundResource(regularBoxBackground)
|
||||
} else {
|
||||
if (completed) {
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
this.checkboxHolder.setBackgroundColor(context.getThemeColor(R.attr.colorWindowBackground))
|
||||
checkboxBackground.setBackgroundResource(completedBoxBackground)
|
||||
} else {
|
||||
this.checkboxHolder.setBackgroundColor(this.taskGray)
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_ternary))
|
||||
checkboxBackground.setBackgroundResource(regularBoxBackground)
|
||||
checkboxBackground.setBackgroundResource(inactiveBoxBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun shouldDisplayAsActive(task: Task?, userID: String?): Boolean
|
||||
|
||||
private fun updateChecklistDisplay() {
|
||||
// This needs to be a LinearLayout, as ListViews can not be inside other ListViews.
|
||||
if (this.shouldDisplayExpandedChecklist()) {
|
||||
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
|
||||
if (this.task?.checklist?.isValid == true) {
|
||||
checklistView.removeAllViews()
|
||||
for (item in this.task?.checklist ?: emptyList<ChecklistItem>()) {
|
||||
val itemView = layoutInflater?.inflate(R.layout.checklist_item_row, this.checklistView, false)
|
||||
val checkboxBackground = itemView?.findViewById<View>(R.id.checkBoxBackground)
|
||||
if (task?.type == TaskType.TODO) {
|
||||
checkboxBackground?.setBackgroundResource(R.drawable.round_checklist_unchecked)
|
||||
}
|
||||
checkboxBackground?.backgroundTintList = ContextCompat.getColorStateList(
|
||||
context,
|
||||
(
|
||||
if (context.isUsingNightModeResources()) {
|
||||
if (task?.completed(userID) == true || (task?.type == TaskType.DAILY && task?.isDue == false)) {
|
||||
R.color.checkbox_fill
|
||||
} else {
|
||||
task?.lightTaskColor
|
||||
}
|
||||
} else {
|
||||
R.color.checkbox_fill
|
||||
}
|
||||
) ?: R.color.checkbox_fill
|
||||
)
|
||||
val textView = itemView?.findViewById<TextView>(R.id.checkedTextView)
|
||||
// Populate the data into the template view using the data object
|
||||
textView?.text = item.text
|
||||
textView?.setTextColor(ContextCompat.getColor(context, if (item.completed) R.color.text_dimmed else R.color.text_secondary))
|
||||
if (item.text != null) {
|
||||
MainScope().launch(Dispatchers.IO) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(item.text ?: "")
|
||||
withContext(Dispatchers.Main) {
|
||||
textView?.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
val checkmark = itemView?.findViewById<ImageView>(R.id.checkmark)
|
||||
checkmark?.drawable?.setTintMode(PorterDuff.Mode.SRC_ATOP)
|
||||
checkmark?.visibility = if (item.completed) View.VISIBLE else View.GONE
|
||||
val checkboxHolder = itemView?.findViewById<View>(R.id.checkBoxHolder) as? ViewGroup
|
||||
checkboxHolder?.setOnClickListener { _ ->
|
||||
task?.let { scoreChecklistItemFunc(it, item) }
|
||||
}
|
||||
val color = ContextCompat.getColor(
|
||||
context,
|
||||
if (task?.completed(userID) == true || (task?.type == TaskType.DAILY && task?.isDue == false)) {
|
||||
checkmark?.drawable?.setTint(ContextCompat.getColor(context, R.color.text_dimmed))
|
||||
R.color.offset_background
|
||||
} else {
|
||||
val color = if (context.isUsingNightModeResources()) task?.extraExtraDarkTaskColor else task?.darkTaskColor
|
||||
checkmark?.drawable?.setTint(ContextCompat.getColor(context, color ?: R.color.text_dimmed))
|
||||
task?.extraLightTaskColor ?: R.color.offset_background
|
||||
}
|
||||
)
|
||||
color.let { checkboxHolder?.setBackgroundColor(it) }
|
||||
this.checklistView.addView(itemView)
|
||||
}
|
||||
}
|
||||
this.checklistView.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.checklistView.removeAllViewsInLayout()
|
||||
this.checklistView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setChecklistIndicatorBackgroundActive(isActive: Boolean) {
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.checklist_indicator_background)
|
||||
if (isActive) {
|
||||
drawable?.setTint(ContextCompat.getColor(context, R.color.gray_200))
|
||||
val textColor = if (context.isUsingNightModeResources()) {
|
||||
ContextCompat.getColor(context, R.color.gray_600)
|
||||
} else {
|
||||
ContextCompat.getColor(context, R.color.gray_500)
|
||||
}
|
||||
checklistCompletedTextView.setTextColor(textColor)
|
||||
checklistAllTextView.setTextColor(textColor)
|
||||
checklistDivider.setBackgroundColor(textColor)
|
||||
} else {
|
||||
drawable?.setTint(ContextCompat.getColor(context, R.color.offset_background))
|
||||
val textColor = ContextCompat.getColor(context, R.color.text_quad)
|
||||
checklistCompletedTextView.setTextColor(textColor)
|
||||
checklistAllTextView.setTextColor(textColor)
|
||||
checklistDivider.setBackgroundColor(textColor)
|
||||
}
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
checklistIndicatorWrapper.background = drawable
|
||||
}
|
||||
|
||||
private fun onChecklistIndicatorClicked() {
|
||||
expandedChecklistRow = if (this.shouldDisplayExpandedChecklist()) null else bindingAdapterPosition
|
||||
if (this.shouldDisplayExpandedChecklist()) {
|
||||
val recyclerView = this.checklistView.parent.parent as? RecyclerView
|
||||
val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager
|
||||
layoutManager?.scrollToPositionWithOffset(this.bindingAdapterPosition, 15)
|
||||
}
|
||||
updateChecklistDisplay()
|
||||
}
|
||||
|
||||
override fun onLeftActionTouched() {
|
||||
super.onLeftActionTouched()
|
||||
if (task?.isValid == true && !isLocked) {
|
||||
onCheckedChanged(!(task?.completed(userID) ?: false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRightActionTouched() {
|
||||
super.onRightActionTouched()
|
||||
onChecklistIndicatorClicked()
|
||||
}
|
||||
|
||||
private fun shouldDisplayExpandedChecklist(): Boolean {
|
||||
return expandedChecklistRow != null && bindingAdapterPosition == expandedChecklistRow
|
||||
}
|
||||
|
||||
private fun onCheckedChanged(isChecked: Boolean) {
|
||||
if (task?.isValid != true) {
|
||||
return
|
||||
}
|
||||
if (isChecked != task?.completed(userID)) {
|
||||
task?.let { scoreTaskFunc(it, if (task?.completed(userID) == false) TaskDirection.UP else TaskDirection.DOWN) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setDisabled(openTaskDisabled: Boolean, taskActionsDisabled: Boolean) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
this.checkboxHolder.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private var expandedChecklistRow: Int? = null
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.common.habitica.extensions.isUsingNightModeResources
|
||||
import com.habitrpg.common.habitica.helpers.MarkdownParser
|
||||
import com.habitrpg.common.habitica.helpers.setParsedMarkdown
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.shared.habitica.models.tasks.TaskType
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.MainScope
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
|
||||
abstract class ChecklistedViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
var scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
private val checkboxHolder: ViewGroup = itemView.findViewById(R.id.checkBoxHolder)
|
||||
private val checkmarkView: ImageView = itemView.findViewById(R.id.checkmark)
|
||||
private val lockView: ImageView = itemView.findViewById(R.id.lock_view)
|
||||
private val checkboxBackground: View = itemView.findViewById(R.id.checkBoxBackground)
|
||||
private val checklistView: LinearLayout = itemView.findViewById(R.id.checklistView)
|
||||
private val checklistIndicatorWrapper: ViewGroup = itemView.findViewById(R.id.checklistIndicatorWrapper)
|
||||
private val checklistCompletedTextView: TextView = itemView.findViewById(R.id.checkListCompletedTextView)
|
||||
private val checklistAllTextView: TextView = itemView.findViewById(R.id.checkListAllTextView)
|
||||
private val checklistDivider: View = itemView.findViewById(R.id.checklistDivider)
|
||||
|
||||
init {
|
||||
checklistIndicatorWrapper.isClickable = true
|
||||
checklistIndicatorWrapper.setOnClickListener { onChecklistIndicatorClicked() }
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
var completed = data.completed(userID)
|
||||
if (data.isPendingApproval) {
|
||||
completed = false
|
||||
}
|
||||
if (isLocked) {
|
||||
this.checkmarkView.visibility = View.GONE
|
||||
this.lockView.visibility = View.VISIBLE
|
||||
val icon = AppCompatResources.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, if (data.isDue == true || data.type == TaskType.TODO) data.extraExtraDarkTaskColor else R.color.text_dimmed))
|
||||
lockView.setImageDrawable(icon)
|
||||
} else {
|
||||
this.checkmarkView.visibility = if (completed) View.VISIBLE else View.GONE
|
||||
checkmarkView.drawable.setTint(ContextCompat.getColor(context, R.color.gray_400))
|
||||
this.lockView.visibility = View.GONE
|
||||
}
|
||||
this.checklistCompletedTextView.text = data.completedChecklistCount.toString()
|
||||
this.checklistAllTextView.text = data.checklist?.size.toString()
|
||||
|
||||
this.checklistView.removeAllViews()
|
||||
this.updateChecklistDisplay()
|
||||
|
||||
this.checklistIndicatorWrapper.visibility = if (data.checklist?.size == 0) View.GONE else View.VISIBLE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
val regularBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_unchecked else R.drawable.todo_unchecked
|
||||
val completedBoxBackground = if (task?.type == TaskType.DAILY) R.drawable.daily_checked else R.drawable.todo_checked
|
||||
val inactiveBoxBackground = R.drawable.daily_inactive
|
||||
if (this.shouldDisplayAsActive(data, userID) && !data.isPendingApproval) {
|
||||
this.checkboxHolder.setBackgroundResource(data.lightTaskColor)
|
||||
checkboxBackground.setBackgroundResource(regularBoxBackground)
|
||||
} else {
|
||||
if (completed) {
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
this.checkboxHolder.setBackgroundColor(context.getThemeColor(R.attr.colorWindowBackground))
|
||||
checkboxBackground.setBackgroundResource(completedBoxBackground)
|
||||
} else {
|
||||
this.checkboxHolder.setBackgroundColor(this.taskGray)
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_ternary))
|
||||
checkboxBackground.setBackgroundResource(regularBoxBackground)
|
||||
checkboxBackground.setBackgroundResource(inactiveBoxBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun shouldDisplayAsActive(
|
||||
task: Task?,
|
||||
userID: String?,
|
||||
): Boolean
|
||||
|
||||
private fun updateChecklistDisplay() {
|
||||
// This needs to be a LinearLayout, as ListViews can not be inside other ListViews.
|
||||
if (this.shouldDisplayExpandedChecklist()) {
|
||||
val layoutInflater = context.getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
|
||||
if (this.task?.checklist?.isValid == true) {
|
||||
checklistView.removeAllViews()
|
||||
for (item in this.task?.checklist ?: emptyList<ChecklistItem>()) {
|
||||
val itemView = layoutInflater?.inflate(R.layout.checklist_item_row, this.checklistView, false)
|
||||
val checkboxBackground = itemView?.findViewById<View>(R.id.checkBoxBackground)
|
||||
if (task?.type == TaskType.TODO) {
|
||||
checkboxBackground?.setBackgroundResource(R.drawable.round_checklist_unchecked)
|
||||
}
|
||||
checkboxBackground?.backgroundTintList =
|
||||
ContextCompat.getColorStateList(
|
||||
context,
|
||||
(
|
||||
if (context.isUsingNightModeResources()) {
|
||||
if (task?.completed(userID) == true || (task?.type == TaskType.DAILY && task?.isDue == false)) {
|
||||
R.color.checkbox_fill
|
||||
} else {
|
||||
task?.lightTaskColor
|
||||
}
|
||||
} else {
|
||||
R.color.checkbox_fill
|
||||
}
|
||||
) ?: R.color.checkbox_fill,
|
||||
)
|
||||
val textView = itemView?.findViewById<TextView>(R.id.checkedTextView)
|
||||
// Populate the data into the template view using the data object
|
||||
textView?.text = item.text
|
||||
textView?.setTextColor(ContextCompat.getColor(context, if (item.completed) R.color.text_dimmed else R.color.text_secondary))
|
||||
if (item.text != null) {
|
||||
MainScope().launch(Dispatchers.IO) {
|
||||
val parsedText = MarkdownParser.parseMarkdown(item.text ?: "")
|
||||
withContext(Dispatchers.Main) {
|
||||
textView?.setParsedMarkdown(parsedText)
|
||||
}
|
||||
}
|
||||
}
|
||||
val checkmark = itemView?.findViewById<ImageView>(R.id.checkmark)
|
||||
checkmark?.drawable?.setTintMode(PorterDuff.Mode.SRC_ATOP)
|
||||
checkmark?.visibility = if (item.completed) View.VISIBLE else View.GONE
|
||||
val checkboxHolder = itemView?.findViewById<View>(R.id.checkBoxHolder) as? ViewGroup
|
||||
checkboxHolder?.setOnClickListener { _ ->
|
||||
task?.let { scoreChecklistItemFunc(it, item) }
|
||||
}
|
||||
val color =
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
if (task?.completed(userID) == true || (task?.type == TaskType.DAILY && task?.isDue == false)) {
|
||||
checkmark?.drawable?.setTint(ContextCompat.getColor(context, R.color.text_dimmed))
|
||||
R.color.offset_background
|
||||
} else {
|
||||
val color = if (context.isUsingNightModeResources()) task?.extraExtraDarkTaskColor else task?.darkTaskColor
|
||||
checkmark?.drawable?.setTint(ContextCompat.getColor(context, color ?: R.color.text_dimmed))
|
||||
task?.extraLightTaskColor ?: R.color.offset_background
|
||||
},
|
||||
)
|
||||
color.let { checkboxHolder?.setBackgroundColor(it) }
|
||||
this.checklistView.addView(itemView)
|
||||
}
|
||||
}
|
||||
this.checklistView.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.checklistView.removeAllViewsInLayout()
|
||||
this.checklistView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
protected fun setChecklistIndicatorBackgroundActive(isActive: Boolean) {
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.checklist_indicator_background)
|
||||
if (isActive) {
|
||||
drawable?.setTint(ContextCompat.getColor(context, R.color.gray_200))
|
||||
val textColor =
|
||||
if (context.isUsingNightModeResources()) {
|
||||
ContextCompat.getColor(context, R.color.gray_600)
|
||||
} else {
|
||||
ContextCompat.getColor(context, R.color.gray_500)
|
||||
}
|
||||
checklistCompletedTextView.setTextColor(textColor)
|
||||
checklistAllTextView.setTextColor(textColor)
|
||||
checklistDivider.setBackgroundColor(textColor)
|
||||
} else {
|
||||
drawable?.setTint(ContextCompat.getColor(context, R.color.offset_background))
|
||||
val textColor = ContextCompat.getColor(context, R.color.text_quad)
|
||||
checklistCompletedTextView.setTextColor(textColor)
|
||||
checklistAllTextView.setTextColor(textColor)
|
||||
checklistDivider.setBackgroundColor(textColor)
|
||||
}
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
checklistIndicatorWrapper.background = drawable
|
||||
}
|
||||
|
||||
private fun onChecklistIndicatorClicked() {
|
||||
expandedChecklistRow = if (this.shouldDisplayExpandedChecklist()) null else bindingAdapterPosition
|
||||
if (this.shouldDisplayExpandedChecklist()) {
|
||||
val recyclerView = this.checklistView.parent.parent as? RecyclerView
|
||||
val layoutManager = recyclerView?.layoutManager as? LinearLayoutManager
|
||||
layoutManager?.scrollToPositionWithOffset(this.bindingAdapterPosition, 15)
|
||||
}
|
||||
updateChecklistDisplay()
|
||||
}
|
||||
|
||||
override fun onLeftActionTouched() {
|
||||
super.onLeftActionTouched()
|
||||
if (task?.isValid == true && !isLocked) {
|
||||
onCheckedChanged(!(task?.completed(userID) ?: false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRightActionTouched() {
|
||||
super.onRightActionTouched()
|
||||
onChecklistIndicatorClicked()
|
||||
}
|
||||
|
||||
private fun shouldDisplayExpandedChecklist(): Boolean {
|
||||
return expandedChecklistRow != null && bindingAdapterPosition == expandedChecklistRow
|
||||
}
|
||||
|
||||
private fun onCheckedChanged(isChecked: Boolean) {
|
||||
if (task?.isValid != true) {
|
||||
return
|
||||
}
|
||||
if (isChecked != task?.completed(userID)) {
|
||||
task?.let { scoreTaskFunc(it, if (task?.completed(userID) == false) TaskDirection.UP else TaskDirection.DOWN) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun setDisabled(
|
||||
openTaskDisabled: Boolean,
|
||||
taskActionsDisabled: Boolean,
|
||||
) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
this.checkboxHolder.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var expandedChecklistRow: Int? = null
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,94 +1,97 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import java.text.DateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
class DailyViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
|
||||
override val taskIconWrapperIsVisible: Boolean
|
||||
get() {
|
||||
var isVisible: Boolean = super.taskIconWrapperIsVisible
|
||||
if (this.streakTextView.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
return isVisible
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?
|
||||
) {
|
||||
this.task = data
|
||||
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
|
||||
|
||||
if (data.reminders?.size == 0) {
|
||||
reminderTextView.visibility = View.GONE
|
||||
} else {
|
||||
reminderTextView.visibility = View.VISIBLE
|
||||
val now = Date()
|
||||
val calendar = Calendar.getInstance()
|
||||
val nextReminder = data.reminders?.firstOrNull {
|
||||
calendar.time = now
|
||||
calendar.set(
|
||||
calendar.get(Calendar.YEAR),
|
||||
calendar.get(Calendar.MONTH),
|
||||
calendar.get(Calendar.DATE),
|
||||
it.getZonedDateTime()?.hour ?: 0,
|
||||
it.getZonedDateTime()?.minute ?: 0,
|
||||
0
|
||||
)
|
||||
now < calendar.time
|
||||
} ?: data.reminders?.first()
|
||||
|
||||
var reminderString = ""
|
||||
if (nextReminder?.time != null) {
|
||||
val time = Date.from(nextReminder.getLocalZonedDateTimeInstant())
|
||||
reminderString += formatter.format(time)
|
||||
}
|
||||
if ((data.reminders?.size ?: 0) > 1) {
|
||||
reminderString = "$reminderString (+${(data.reminders?.size ?: 0) - 1})"
|
||||
}
|
||||
reminderTextView.text = reminderString
|
||||
}
|
||||
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
}
|
||||
|
||||
override fun shouldDisplayAsActive(task: Task?, userID: String?): Boolean {
|
||||
return task?.isDisplayedActiveForUser(userID) ?: false
|
||||
}
|
||||
|
||||
override fun configureSpecialTaskTextView(task: Task) {
|
||||
super.configureSpecialTaskTextView(task)
|
||||
if ((task.streak ?: 0) > 0 && !task.isGroupTask) {
|
||||
this.streakTextView.text = task.streak.toString()
|
||||
this.streakTextView.visibility = View.VISIBLE
|
||||
this.streakIconView.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.streakTextView.visibility = View.GONE
|
||||
this.streakIconView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val formatter: DateFormat
|
||||
get() {
|
||||
return DateFormat.getTimeInstance(DateFormat.SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
import java.text.DateFormat
|
||||
import java.util.Calendar
|
||||
import java.util.Date
|
||||
|
||||
class DailyViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
override val taskIconWrapperIsVisible: Boolean
|
||||
get() {
|
||||
var isVisible: Boolean = super.taskIconWrapperIsVisible
|
||||
if (this.streakTextView.visibility == View.VISIBLE) {
|
||||
isVisible = true
|
||||
}
|
||||
return isVisible
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
this.task = data
|
||||
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
|
||||
|
||||
if (data.reminders?.size == 0) {
|
||||
reminderTextView.visibility = View.GONE
|
||||
} else {
|
||||
reminderTextView.visibility = View.VISIBLE
|
||||
val now = Date()
|
||||
val calendar = Calendar.getInstance()
|
||||
val nextReminder =
|
||||
data.reminders?.firstOrNull {
|
||||
calendar.time = now
|
||||
calendar.set(
|
||||
calendar.get(Calendar.YEAR),
|
||||
calendar.get(Calendar.MONTH),
|
||||
calendar.get(Calendar.DATE),
|
||||
it.getZonedDateTime()?.hour ?: 0,
|
||||
it.getZonedDateTime()?.minute ?: 0,
|
||||
0,
|
||||
)
|
||||
now < calendar.time
|
||||
} ?: data.reminders?.first()
|
||||
|
||||
var reminderString = ""
|
||||
if (nextReminder?.time != null) {
|
||||
val time = Date.from(nextReminder.getLocalZonedDateTimeInstant())
|
||||
reminderString += formatter.format(time)
|
||||
}
|
||||
if ((data.reminders?.size ?: 0) > 1) {
|
||||
reminderString = "$reminderString (+${(data.reminders?.size ?: 0) - 1})"
|
||||
}
|
||||
reminderTextView.text = reminderString
|
||||
}
|
||||
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
}
|
||||
|
||||
override fun shouldDisplayAsActive(
|
||||
task: Task?,
|
||||
userID: String?,
|
||||
): Boolean {
|
||||
return task?.isDisplayedActiveForUser(userID) ?: false
|
||||
}
|
||||
|
||||
override fun configureSpecialTaskTextView(task: Task) {
|
||||
super.configureSpecialTaskTextView(task)
|
||||
if ((task.streak ?: 0) > 0 && !task.isGroupTask) {
|
||||
this.streakTextView.text = task.streak.toString()
|
||||
this.streakTextView.visibility = View.VISIBLE
|
||||
this.streakIconView.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.streakTextView.visibility = View.GONE
|
||||
this.streakIconView.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private val formatter: DateFormat
|
||||
get() {
|
||||
return DateFormat.getTimeInstance(DateFormat.SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,168 +1,174 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class HabitViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
|
||||
private val btnPlusWrapper: FrameLayout = itemView.findViewById(R.id.btnPlusWrapper)
|
||||
private val btnPlusIconView: ImageView = itemView.findViewById(R.id.btnPlusIconView)
|
||||
private val btnPlusCircleView: View = itemView.findViewById(R.id.button_plus_circle_view)
|
||||
private val btnPlus: Button = itemView.findViewById(R.id.btnPlus)
|
||||
private val btnMinusWrapper: FrameLayout = itemView.findViewById(R.id.btnMinusWrapper)
|
||||
private val btnMinusIconView: ImageView = itemView.findViewById(R.id.btnMinusIconView)
|
||||
private val btnMinusCircleView: View = itemView.findViewById(R.id.button_minus_circle_view)
|
||||
private val btnMinus: Button = itemView.findViewById(R.id.btnMinus)
|
||||
|
||||
init {
|
||||
btnPlus.setOnClickListener { onPlusButtonClicked() }
|
||||
btnPlus.isClickable = true
|
||||
btnMinus.setOnClickListener { onMinusButtonClicked() }
|
||||
btnMinus.isClickable = true
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?
|
||||
) {
|
||||
this.task = data
|
||||
if (data.up == true) {
|
||||
val plusIcon = if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, data.extraExtraDarkTaskColor))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_plus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.white))
|
||||
icon
|
||||
}
|
||||
plusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnPlusIconView.setImageDrawable(plusIcon)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.habit_circle)
|
||||
this.btnPlusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
drawable?.setTint(ContextCompat.getColor(context, data.mediumTaskColor))
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
btnPlusCircleView.background = drawable
|
||||
this.btnPlus.visibility = View.VISIBLE
|
||||
this.btnPlus.isClickable = true
|
||||
} else {
|
||||
this.btnPlusWrapper.setBackgroundResource(R.color.habit_inactive_gray)
|
||||
val plusIcon = if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.text_dimmed))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_plus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
}
|
||||
plusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnPlusIconView.setImageDrawable(plusIcon)
|
||||
btnPlusCircleView.background = ContextCompat.getDrawable(context, R.drawable.habit_circle_disabled)
|
||||
this.btnPlus.visibility = View.GONE
|
||||
this.btnPlus.isClickable = false
|
||||
}
|
||||
|
||||
if (data.down == true) {
|
||||
this.btnMinusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
val minusIcon = if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, data.extraExtraDarkTaskColor))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_minus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.white))
|
||||
icon
|
||||
}
|
||||
minusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnMinusIconView.setImageDrawable(minusIcon)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.habit_circle)
|
||||
this.btnMinusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
drawable?.setTint(ContextCompat.getColor(context, data.mediumTaskColor))
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
btnMinusCircleView.background = drawable
|
||||
this.btnMinus.visibility = View.VISIBLE
|
||||
this.btnMinus.isClickable = true
|
||||
} else {
|
||||
this.btnMinusWrapper.setBackgroundResource(R.color.habit_inactive_gray)
|
||||
val minusIcon = if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_minus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
}
|
||||
minusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnMinusIconView.setImageDrawable(minusIcon)
|
||||
btnMinusCircleView.background = ContextCompat.getDrawable(context, R.drawable.habit_circle_disabled)
|
||||
this.btnMinus.visibility = View.GONE
|
||||
this.btnMinus.isClickable = false
|
||||
}
|
||||
|
||||
val streakString = task?.streakString
|
||||
if (streakString?.isNotEmpty() == true && task?.isGroupTask != true) {
|
||||
streakTextView.text = streakString
|
||||
streakTextView.visibility = View.VISIBLE
|
||||
streakIconView.visibility = View.VISIBLE
|
||||
} else {
|
||||
streakTextView.visibility = View.GONE
|
||||
streakIconView.visibility = View.GONE
|
||||
}
|
||||
reminderTextView.visibility = View.GONE
|
||||
calendarIconView?.visibility = View.GONE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
if (data.up == false && data.down == false) {
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLeftActionTouched() {
|
||||
super.onLeftActionTouched()
|
||||
if (!isLocked) {
|
||||
onPlusButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRightActionTouched() {
|
||||
super.onRightActionTouched()
|
||||
if (!isLocked) {
|
||||
onMinusButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPlusButtonClicked() {
|
||||
if (task?.up != true) return
|
||||
task?.let { scoreTaskFunc.invoke(it, TaskDirection.UP) }
|
||||
}
|
||||
|
||||
private fun onMinusButtonClicked() {
|
||||
if (task?.down != true) return
|
||||
task?.let { scoreTaskFunc.invoke(it, TaskDirection.DOWN) }
|
||||
}
|
||||
|
||||
override fun setDisabled(openTaskDisabled: Boolean, taskActionsDisabled: Boolean) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
|
||||
this.btnPlus.isEnabled = !taskActionsDisabled
|
||||
this.btnMinus.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.graphics.PorterDuff
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import android.widget.FrameLayout
|
||||
import android.widget.ImageView
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class HabitViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : BaseTaskViewHolder(itemView, scoreTaskFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
private val btnPlusWrapper: FrameLayout = itemView.findViewById(R.id.btnPlusWrapper)
|
||||
private val btnPlusIconView: ImageView = itemView.findViewById(R.id.btnPlusIconView)
|
||||
private val btnPlusCircleView: View = itemView.findViewById(R.id.button_plus_circle_view)
|
||||
private val btnPlus: Button = itemView.findViewById(R.id.btnPlus)
|
||||
private val btnMinusWrapper: FrameLayout = itemView.findViewById(R.id.btnMinusWrapper)
|
||||
private val btnMinusIconView: ImageView = itemView.findViewById(R.id.btnMinusIconView)
|
||||
private val btnMinusCircleView: View = itemView.findViewById(R.id.button_minus_circle_view)
|
||||
private val btnMinus: Button = itemView.findViewById(R.id.btnMinus)
|
||||
|
||||
init {
|
||||
btnPlus.setOnClickListener { onPlusButtonClicked() }
|
||||
btnPlus.isClickable = true
|
||||
btnMinus.setOnClickListener { onMinusButtonClicked() }
|
||||
btnMinus.isClickable = true
|
||||
}
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
this.task = data
|
||||
if (data.up == true) {
|
||||
val plusIcon =
|
||||
if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, data.extraExtraDarkTaskColor))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_plus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.white))
|
||||
icon
|
||||
}
|
||||
plusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnPlusIconView.setImageDrawable(plusIcon)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.habit_circle)
|
||||
this.btnPlusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
drawable?.setTint(ContextCompat.getColor(context, data.mediumTaskColor))
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
btnPlusCircleView.background = drawable
|
||||
this.btnPlus.visibility = View.VISIBLE
|
||||
this.btnPlus.isClickable = true
|
||||
} else {
|
||||
this.btnPlusWrapper.setBackgroundResource(R.color.habit_inactive_gray)
|
||||
val plusIcon =
|
||||
if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.text_dimmed))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_plus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
}
|
||||
plusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnPlusIconView.setImageDrawable(plusIcon)
|
||||
btnPlusCircleView.background = ContextCompat.getDrawable(context, R.drawable.habit_circle_disabled)
|
||||
this.btnPlus.visibility = View.GONE
|
||||
this.btnPlus.isClickable = false
|
||||
}
|
||||
|
||||
if (data.down == true) {
|
||||
this.btnMinusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
val minusIcon =
|
||||
if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, data.extraExtraDarkTaskColor))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_minus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.white))
|
||||
icon
|
||||
}
|
||||
minusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnMinusIconView.setImageDrawable(minusIcon)
|
||||
val drawable = ContextCompat.getDrawable(context, R.drawable.habit_circle)
|
||||
this.btnMinusWrapper.setBackgroundResource(data.lightTaskColor)
|
||||
drawable?.setTint(ContextCompat.getColor(context, data.mediumTaskColor))
|
||||
drawable?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
btnMinusCircleView.background = drawable
|
||||
this.btnMinus.visibility = View.VISIBLE
|
||||
this.btnMinus.isClickable = true
|
||||
} else {
|
||||
this.btnMinusWrapper.setBackgroundResource(R.color.habit_inactive_gray)
|
||||
val minusIcon =
|
||||
if (isLocked) {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.task_lock)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
} else {
|
||||
val icon = ContextCompat.getDrawable(context, R.drawable.habit_minus)
|
||||
icon?.setTint(ContextCompat.getColor(context, R.color.content_background_offset))
|
||||
icon
|
||||
}
|
||||
minusIcon?.setTintMode(PorterDuff.Mode.MULTIPLY)
|
||||
this.btnMinusIconView.setImageDrawable(minusIcon)
|
||||
btnMinusCircleView.background = ContextCompat.getDrawable(context, R.drawable.habit_circle_disabled)
|
||||
this.btnMinus.visibility = View.GONE
|
||||
this.btnMinus.isClickable = false
|
||||
}
|
||||
|
||||
val streakString = task?.streakString
|
||||
if (streakString?.isNotEmpty() == true && task?.isGroupTask != true) {
|
||||
streakTextView.text = streakString
|
||||
streakTextView.visibility = View.VISIBLE
|
||||
streakIconView.visibility = View.VISIBLE
|
||||
} else {
|
||||
streakTextView.visibility = View.GONE
|
||||
streakIconView.visibility = View.GONE
|
||||
}
|
||||
reminderTextView.visibility = View.GONE
|
||||
calendarIconView?.visibility = View.GONE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
if (data.up == false && data.down == false) {
|
||||
titleTextView.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
notesTextView?.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLeftActionTouched() {
|
||||
super.onLeftActionTouched()
|
||||
if (!isLocked) {
|
||||
onPlusButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRightActionTouched() {
|
||||
super.onRightActionTouched()
|
||||
if (!isLocked) {
|
||||
onMinusButtonClicked()
|
||||
}
|
||||
}
|
||||
|
||||
private fun onPlusButtonClicked() {
|
||||
if (task?.up != true) return
|
||||
task?.let { scoreTaskFunc.invoke(it, TaskDirection.UP) }
|
||||
}
|
||||
|
||||
private fun onMinusButtonClicked() {
|
||||
if (task?.down != true) return
|
||||
task?.let { scoreTaskFunc.invoke(it, TaskDirection.DOWN) }
|
||||
}
|
||||
|
||||
override fun setDisabled(
|
||||
openTaskDisabled: Boolean,
|
||||
taskActionsDisabled: Boolean,
|
||||
) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
|
||||
this.btnPlus.isEnabled = !taskActionsDisabled
|
||||
this.btnMinus.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,97 +1,106 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.RewardItemCardBinding
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.helpers.NumberAbbreviator
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class RewardViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : BaseTaskViewHolder(
|
||||
itemView,
|
||||
scoreTaskFunc,
|
||||
openTaskFunc,
|
||||
brokenTaskFunc,
|
||||
assignedTextProvider
|
||||
) {
|
||||
private val binding = RewardItemCardBinding.bind(itemView)
|
||||
|
||||
init {
|
||||
binding.buyButton.setOnClickListener {
|
||||
buyReward()
|
||||
}
|
||||
binding.goldIcon.setImageBitmap(HabiticaIconsHelper.imageOfGold())
|
||||
}
|
||||
|
||||
private fun buyReward() {
|
||||
task?.let { scoreTaskFunc(it, TaskDirection.DOWN) }
|
||||
}
|
||||
|
||||
override fun setDisabled(openTaskDisabled: Boolean, taskActionsDisabled: Boolean) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
binding.buyButton.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
|
||||
fun bind(reward: Task, position: Int, canBuy: Boolean, displayMode: String, ownerID: String?) {
|
||||
this.task = reward
|
||||
streakTextView.visibility = View.GONE
|
||||
super.bind(reward, position, displayMode, ownerID)
|
||||
binding.priceLabel.text =
|
||||
NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0)
|
||||
|
||||
if (isLocked) {
|
||||
binding.priceLabel.setCompoundDrawablesWithIntrinsicBounds(
|
||||
HabiticaIconsHelper.imageOfLocked(
|
||||
ContextCompat.getColor(context, R.color.gray_1_30),
|
||||
10,
|
||||
12
|
||||
).toDrawable(context.resources),
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
binding.priceLabel.compoundDrawablePadding = 2.dpToPx(context)
|
||||
} else {
|
||||
binding.priceLabel.setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
if (canBuy && !isLocked) {
|
||||
binding.goldIcon.alpha = 1.0f
|
||||
binding.priceLabel.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.reward_buy_button_text
|
||||
)
|
||||
)
|
||||
binding.buyButton.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.reward_buy_button_bg
|
||||
)
|
||||
)
|
||||
} else {
|
||||
binding.goldIcon.alpha = 0.6f
|
||||
binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
binding.buyButton.setBackgroundColor(
|
||||
ColorUtils.setAlphaComponent(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.offset_background
|
||||
),
|
||||
127
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.ColorUtils
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.RewardItemCardBinding
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.helpers.NumberAbbreviator
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class RewardViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : BaseTaskViewHolder(
|
||||
itemView,
|
||||
scoreTaskFunc,
|
||||
openTaskFunc,
|
||||
brokenTaskFunc,
|
||||
assignedTextProvider,
|
||||
) {
|
||||
private val binding = RewardItemCardBinding.bind(itemView)
|
||||
|
||||
init {
|
||||
binding.buyButton.setOnClickListener {
|
||||
buyReward()
|
||||
}
|
||||
binding.goldIcon.setImageBitmap(HabiticaIconsHelper.imageOfGold())
|
||||
}
|
||||
|
||||
private fun buyReward() {
|
||||
task?.let { scoreTaskFunc(it, TaskDirection.DOWN) }
|
||||
}
|
||||
|
||||
override fun setDisabled(
|
||||
openTaskDisabled: Boolean,
|
||||
taskActionsDisabled: Boolean,
|
||||
) {
|
||||
super.setDisabled(openTaskDisabled, taskActionsDisabled)
|
||||
binding.buyButton.isEnabled = !taskActionsDisabled
|
||||
}
|
||||
|
||||
fun bind(
|
||||
reward: Task,
|
||||
position: Int,
|
||||
canBuy: Boolean,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
this.task = reward
|
||||
streakTextView.visibility = View.GONE
|
||||
super.bind(reward, position, displayMode, ownerID)
|
||||
binding.priceLabel.text =
|
||||
NumberAbbreviator.abbreviate(itemView.context, this.task?.value ?: 0.0)
|
||||
|
||||
if (isLocked) {
|
||||
binding.priceLabel.setCompoundDrawablesWithIntrinsicBounds(
|
||||
HabiticaIconsHelper.imageOfLocked(
|
||||
ContextCompat.getColor(context, R.color.gray_1_30),
|
||||
10,
|
||||
12,
|
||||
).toDrawable(context.resources),
|
||||
null,
|
||||
null,
|
||||
null,
|
||||
)
|
||||
binding.priceLabel.compoundDrawablePadding = 2.dpToPx(context)
|
||||
} else {
|
||||
binding.priceLabel.setCompoundDrawables(null, null, null, null)
|
||||
}
|
||||
if (canBuy && !isLocked) {
|
||||
binding.goldIcon.alpha = 1.0f
|
||||
binding.priceLabel.setTextColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.reward_buy_button_text,
|
||||
),
|
||||
)
|
||||
binding.buyButton.setBackgroundColor(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.reward_buy_button_bg,
|
||||
),
|
||||
)
|
||||
} else {
|
||||
binding.goldIcon.alpha = 0.6f
|
||||
binding.priceLabel.setTextColor(ContextCompat.getColor(context, R.color.text_quad))
|
||||
binding.buyButton.setBackgroundColor(
|
||||
ColorUtils.setAlphaComponent(
|
||||
ContextCompat.getColor(
|
||||
context,
|
||||
R.color.offset_background,
|
||||
),
|
||||
127,
|
||||
),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,56 +1,58 @@
|
|||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.formatForLocale
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class TodoViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?
|
||||
) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?
|
||||
) {
|
||||
this.task = data
|
||||
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
|
||||
reminderTextView.visibility = View.GONE
|
||||
this.streakTextView.visibility = View.GONE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
}
|
||||
|
||||
override fun configureSpecialTaskTextView(task: Task) {
|
||||
super.configureSpecialTaskTextView(task)
|
||||
if (task.dueDate != null) {
|
||||
if (task.isDueToday() == true) {
|
||||
specialTaskTextView?.text = context.getString(R.string.today)
|
||||
} else if (task.isDayOrMorePastDue() == true) {
|
||||
task.dueDate?.let { specialTaskTextView?.text = it.formatForLocale() }
|
||||
specialTaskTextView?.setTextColor(ContextCompat.getColor(context, R.color.maroon100_red100))
|
||||
} else {
|
||||
task.dueDate?.let { specialTaskTextView?.text = it.formatForLocale() }
|
||||
specialTaskTextView?.setTextColor(ContextCompat.getColor(context, R.color.gray_300))
|
||||
}
|
||||
this.specialTaskTextView?.visibility = View.VISIBLE
|
||||
calendarIconView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.specialTaskTextView?.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldDisplayAsActive(task: Task?, userID: String?): Boolean {
|
||||
return task?.completed(userID) != true
|
||||
}
|
||||
}
|
||||
package com.habitrpg.android.habitica.ui.viewHolders.tasks
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.extensions.formatForLocale
|
||||
import com.habitrpg.android.habitica.helpers.GroupPlanInfoProvider
|
||||
import com.habitrpg.android.habitica.models.tasks.ChecklistItem
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.shared.habitica.models.responses.TaskDirection
|
||||
|
||||
class TodoViewHolder(
|
||||
itemView: View,
|
||||
scoreTaskFunc: ((Task, TaskDirection) -> Unit),
|
||||
scoreChecklistItemFunc: ((Task, ChecklistItem) -> Unit),
|
||||
openTaskFunc: ((Task, View) -> Unit),
|
||||
brokenTaskFunc: ((Task) -> Unit),
|
||||
assignedTextProvider: GroupPlanInfoProvider?,
|
||||
) : ChecklistedViewHolder(itemView, scoreTaskFunc, scoreChecklistItemFunc, openTaskFunc, brokenTaskFunc, assignedTextProvider) {
|
||||
override fun bind(
|
||||
data: Task,
|
||||
position: Int,
|
||||
displayMode: String,
|
||||
ownerID: String?,
|
||||
) {
|
||||
this.task = data
|
||||
setChecklistIndicatorBackgroundActive(data.isChecklistDisplayActive)
|
||||
reminderTextView.visibility = View.GONE
|
||||
this.streakTextView.visibility = View.GONE
|
||||
super.bind(data, position, displayMode, ownerID)
|
||||
}
|
||||
|
||||
override fun configureSpecialTaskTextView(task: Task) {
|
||||
super.configureSpecialTaskTextView(task)
|
||||
if (task.dueDate != null) {
|
||||
if (task.isDueToday() == true) {
|
||||
specialTaskTextView?.text = context.getString(R.string.today)
|
||||
} else if (task.isDayOrMorePastDue() == true) {
|
||||
task.dueDate?.let { specialTaskTextView?.text = it.formatForLocale() }
|
||||
specialTaskTextView?.setTextColor(ContextCompat.getColor(context, R.color.maroon100_red100))
|
||||
} else {
|
||||
task.dueDate?.let { specialTaskTextView?.text = it.formatForLocale() }
|
||||
specialTaskTextView?.setTextColor(ContextCompat.getColor(context, R.color.gray_300))
|
||||
}
|
||||
this.specialTaskTextView?.visibility = View.VISIBLE
|
||||
calendarIconView?.visibility = View.VISIBLE
|
||||
} else {
|
||||
this.specialTaskTextView?.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun shouldDisplayAsActive(
|
||||
task: Task?,
|
||||
userID: String?,
|
||||
): Boolean {
|
||||
return task?.completed(userID) != true
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,14 @@ class NPCBannerView(context: Context, attrs: AttributeSet?) : FrameLayout(contex
|
|||
var shopSpriteSuffix: String? = null
|
||||
set(value) {
|
||||
|
||||
field = if (value.isNullOrEmpty()) {
|
||||
""
|
||||
} else if (value.startsWith("_")) {
|
||||
value
|
||||
} else {
|
||||
"_$value"
|
||||
}
|
||||
field =
|
||||
if (value.isNullOrEmpty()) {
|
||||
""
|
||||
} else if (value.startsWith("_")) {
|
||||
value
|
||||
} else {
|
||||
"_$value"
|
||||
}
|
||||
if (identifier.isNotEmpty()) {
|
||||
setImage()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -34,7 +34,6 @@ import kotlinx.coroutines.launch
|
|||
import java.lang.ref.WeakReference
|
||||
|
||||
open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.HabiticaAlertDialogTheme) {
|
||||
|
||||
var buttonAxis: Int = LinearLayout.VERTICAL
|
||||
set(value) {
|
||||
field = value
|
||||
|
|
@ -186,7 +185,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
isPrimary: Boolean,
|
||||
isDestructive: Boolean = false,
|
||||
autoDismiss: Boolean = true,
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null,
|
||||
): Button {
|
||||
return addButton(context.getString(stringRes), isPrimary, isDestructive, autoDismiss, function)
|
||||
}
|
||||
|
|
@ -196,21 +195,22 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
isPrimary: Boolean,
|
||||
isDestructive: Boolean = false,
|
||||
autoDismiss: Boolean = true,
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null,
|
||||
): Button {
|
||||
val button: Button = if (isPrimary) {
|
||||
if (isDestructive) {
|
||||
binding.buttonsWrapper.inflate(R.layout.dialog_habitica_primary_destructive_button) as? Button
|
||||
val button: Button =
|
||||
if (isPrimary) {
|
||||
if (isDestructive) {
|
||||
binding.buttonsWrapper.inflate(R.layout.dialog_habitica_primary_destructive_button) as? Button
|
||||
} else {
|
||||
binding.buttonsWrapper.inflate(R.layout.dialog_habitica_primary_button) as? Button
|
||||
}
|
||||
} else {
|
||||
binding.buttonsWrapper.inflate(R.layout.dialog_habitica_primary_button) as? Button
|
||||
}
|
||||
} else {
|
||||
val button = binding.buttonsWrapper.inflate(R.layout.dialog_habitica_secondary_button) as? Button
|
||||
if (isDestructive) {
|
||||
button?.setTextColor(ContextCompat.getColor(context, R.color.maroon_100))
|
||||
}
|
||||
button
|
||||
} ?: Button(context)
|
||||
val button = binding.buttonsWrapper.inflate(R.layout.dialog_habitica_secondary_button) as? Button
|
||||
if (isDestructive) {
|
||||
button?.setTextColor(ContextCompat.getColor(context, R.color.maroon_100))
|
||||
}
|
||||
button
|
||||
} ?: Button(context)
|
||||
button.text = string
|
||||
button.elevation = 0f
|
||||
return addButton(button, autoDismiss, function) as Button
|
||||
|
|
@ -219,7 +219,7 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
fun addButton(
|
||||
buttonView: View,
|
||||
autoDismiss: Boolean = true,
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null
|
||||
function: ((HabiticaAlertDialog, Int) -> Unit)? = null,
|
||||
): View {
|
||||
val weakThis = WeakReference(this)
|
||||
val buttonIndex = binding.buttonsWrapper.childCount
|
||||
|
|
@ -241,13 +241,14 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
}
|
||||
|
||||
private fun configureButtonLayoutParams(buttonView: View) {
|
||||
val layoutParams = if (isScrollingLayout) {
|
||||
val params = LinearLayout.LayoutParams(0, 48.dpToPx(context))
|
||||
params.weight = 1f
|
||||
params
|
||||
} else {
|
||||
LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 48.dpToPx(context))
|
||||
}
|
||||
val layoutParams =
|
||||
if (isScrollingLayout) {
|
||||
val params = LinearLayout.LayoutParams(0, 48.dpToPx(context))
|
||||
params.weight = 1f
|
||||
params
|
||||
} else {
|
||||
LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 48.dpToPx(context))
|
||||
}
|
||||
buttonView.layoutParams = layoutParams
|
||||
buttonView.elevation = 10f
|
||||
|
||||
|
|
@ -317,8 +318,8 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
(dialogQueue[0].context as? BaseActivity)?.lifecycleScope?.launch(context = Dispatchers.Main) {
|
||||
delay(500L)
|
||||
if (dialogQueue.size > 0 && (
|
||||
(dialogQueue[0].context as? Activity)?.isFinishing == false ||
|
||||
((dialogQueue[0].context as? ContextThemeWrapper)?.baseContext as? Activity)?.isFinishing == false
|
||||
(dialogQueue[0].context as? Activity)?.isFinishing == false ||
|
||||
((dialogQueue[0].context as? ContextThemeWrapper)?.baseContext as? Activity)?.isFinishing == false
|
||||
)
|
||||
) {
|
||||
dialogQueue[0].show()
|
||||
|
|
|
|||
|
|
@ -65,9 +65,8 @@ class PurchaseDialog(
|
|||
private val userRepository: UserRepository,
|
||||
private val inventoryRepository: InventoryRepository,
|
||||
val item: ShopItem,
|
||||
private val parentActivity: AppCompatActivity? = null
|
||||
private val parentActivity: AppCompatActivity? = null,
|
||||
) : HabiticaAlertDialog(context) {
|
||||
|
||||
private val customHeader: View by lazy {
|
||||
DialogPurchaseShopitemHeaderBinding.inflate(context.layoutInflater).root
|
||||
}
|
||||
|
|
@ -205,28 +204,30 @@ class PurchaseDialog(
|
|||
if (user == null) return
|
||||
val userLvl = user?.stats?.lvl ?: 0
|
||||
if (shopItem.habitClass != null && shopItem.habitClass != "special" && shopItem.habitClass != "armoire" && user?.stats?.habitClass != shopItem.habitClass) {
|
||||
limitedTextView.text = if (userLvl >= 10) {
|
||||
context.getString(R.string.class_equipment_shop_dialog)
|
||||
} else {
|
||||
context.getString(R.string.insufficient_level_equipment_dialog)
|
||||
}
|
||||
limitedTextView.text =
|
||||
if (userLvl >= 10) {
|
||||
context.getString(R.string.class_equipment_shop_dialog)
|
||||
} else {
|
||||
context.getString(R.string.insufficient_level_equipment_dialog)
|
||||
}
|
||||
limitedTextView.visibility = View.VISIBLE
|
||||
limitedTextView.setBackgroundColor(ContextCompat.getColor(context, R.color.inverted_background))
|
||||
} else if (shopItem.event?.end != null) {
|
||||
limitedTextViewJob?.cancel()
|
||||
limitedTextViewJob = MainScope().launch(Dispatchers.Main) {
|
||||
limitedTextView.visibility = View.VISIBLE
|
||||
while (shopItem.event?.end?.after(Date()) == true) {
|
||||
limitedTextView.text = context.getString(R.string.available_for, shopItem.event?.end?.getShortRemainingString())
|
||||
val diff = (shopItem.event?.end?.time ?: 0) - Date().time
|
||||
delay(1.toDuration(if (diff < (60 * 60 * 1000)) DurationUnit.SECONDS else DurationUnit.MINUTES))
|
||||
limitedTextViewJob =
|
||||
MainScope().launch(Dispatchers.Main) {
|
||||
limitedTextView.visibility = View.VISIBLE
|
||||
while (shopItem.event?.end?.after(Date()) == true) {
|
||||
limitedTextView.text = context.getString(R.string.available_for, shopItem.event?.end?.getShortRemainingString())
|
||||
val diff = (shopItem.event?.end?.time ?: 0) - Date().time
|
||||
delay(1.toDuration(if (diff < (60 * 60 * 1000)) DurationUnit.SECONDS else DurationUnit.MINUTES))
|
||||
}
|
||||
if (shopItem.event?.end?.before(Date()) == true) {
|
||||
limitedTextView.text = context.getString(R.string.no_longer_available)
|
||||
limitedTextView.background = ContextCompat.getColor(context, R.color.offset_background).toDrawable()
|
||||
limitedTextView.setTextColor(ContextCompat.getColor(context, R.color.text_secondary))
|
||||
}
|
||||
}
|
||||
if (shopItem.event?.end?.before(Date()) == true) {
|
||||
limitedTextView.text = context.getString(R.string.no_longer_available)
|
||||
limitedTextView.background = ContextCompat.getColor(context, R.color.offset_background).toDrawable()
|
||||
limitedTextView.setTextColor(ContextCompat.getColor(context, R.color.text_secondary))
|
||||
}
|
||||
}
|
||||
} else if (shopItem.locked) {
|
||||
buyLabel.text = context.getString(R.string.locked)
|
||||
limitedTextView.visibility = View.GONE
|
||||
|
|
@ -273,9 +274,10 @@ class PurchaseDialog(
|
|||
pinTextView = customHeader.findViewById(R.id.pin_text)
|
||||
|
||||
addCloseButton()
|
||||
buyButton = addButton(DialogPurchaseShopitemButtonBinding.inflate(layoutInflater).root, autoDismiss = false) { _, _ ->
|
||||
onBuyButtonClicked()
|
||||
}
|
||||
buyButton =
|
||||
addButton(DialogPurchaseShopitemButtonBinding.inflate(layoutInflater).root, autoDismiss = false) { _, _ ->
|
||||
onBuyButtonClicked()
|
||||
}
|
||||
priceLabel = buyButton.findViewById(R.id.priceLabel)
|
||||
priceLabel.animationDuration = 0L
|
||||
buyLabel = buyButton.findViewById(R.id.buy_label)
|
||||
|
|
@ -376,9 +378,10 @@ class PurchaseDialog(
|
|||
if (user?.isSubscribed == true) {
|
||||
InsufficientHourglassesDialog(context).show()
|
||||
} else {
|
||||
val subscriptionBottomSheet = EventOutcomeSubscriptionBottomSheetFragment().apply {
|
||||
eventType = EventOutcomeSubscriptionBottomSheetFragment.EVENT_HOURGLASS_SHOP_OPENED
|
||||
}
|
||||
val subscriptionBottomSheet =
|
||||
EventOutcomeSubscriptionBottomSheetFragment().apply {
|
||||
eventType = EventOutcomeSubscriptionBottomSheetFragment.EVENT_HOURGLASS_SHOP_OPENED
|
||||
}
|
||||
parentActivity?.let { activity -> subscriptionBottomSheet.show(activity.supportFragmentManager, SubscriptionBottomSheetFragment.TAG) }
|
||||
}
|
||||
}
|
||||
|
|
@ -396,18 +399,19 @@ class PurchaseDialog(
|
|||
bundleOf(
|
||||
Pair("shop", shopIdentifier),
|
||||
Pair("type", shopItem.purchaseType),
|
||||
Pair("key", shopItem.key)
|
||||
)
|
||||
Pair("key", shopItem.key),
|
||||
),
|
||||
)
|
||||
HapticFeedbackManager.tap(buyButton)
|
||||
val snackbarText = arrayOf("")
|
||||
val observable: (suspend () -> Any?)
|
||||
if (shopIdentifier != null && shopIdentifier == Shop.TIME_TRAVELERS_SHOP || "mystery_set" == shopItem.purchaseType || shopItem.currency == "hourglasses") {
|
||||
observable = if (shopItem.purchaseType == "gear") {
|
||||
{ inventoryRepository.purchaseMysterySet(shopItem.key) }
|
||||
} else {
|
||||
{ inventoryRepository.purchaseHourglassItem(shopItem.purchaseType, shopItem.key) }
|
||||
}
|
||||
observable =
|
||||
if (shopItem.purchaseType == "gear") {
|
||||
{ inventoryRepository.purchaseMysterySet(shopItem.key) }
|
||||
} else {
|
||||
{ inventoryRepository.purchaseHourglassItem(shopItem.purchaseType, shopItem.key) }
|
||||
}
|
||||
} else if (shopItem.purchaseType == "fortify") {
|
||||
observable = { userRepository.reroll() }
|
||||
} else if (shopItem.purchaseType == "quests" && shopItem.currency == "gold") {
|
||||
|
|
@ -432,8 +436,8 @@ class PurchaseDialog(
|
|||
buyResponse.armoire["type"] ?: "",
|
||||
buyResponse.armoire["dropText"] ?: "",
|
||||
buyResponse.armoire["dropKey"] ?: "",
|
||||
buyResponse.armoire["value"] ?: ""
|
||||
).arguments
|
||||
buyResponse.armoire["value"] ?: "",
|
||||
).arguments,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
@ -442,26 +446,28 @@ class PurchaseDialog(
|
|||
}
|
||||
lifecycleScope.launchCatching {
|
||||
observable()
|
||||
val text = snackbarText[0].ifBlank {
|
||||
if (shopItem.text?.isNotBlank() == true) {
|
||||
context.getString(R.string.successful_purchase, shopItem.text)
|
||||
} else {
|
||||
context.getString(R.string.purchased)
|
||||
val text =
|
||||
snackbarText[0].ifBlank {
|
||||
if (shopItem.text?.isNotBlank() == true) {
|
||||
context.getString(R.string.successful_purchase, shopItem.text)
|
||||
} else {
|
||||
context.getString(R.string.purchased)
|
||||
}
|
||||
}
|
||||
val rightTextColor =
|
||||
when (item.currency) {
|
||||
"gold" -> ContextCompat.getColor(context, R.color.yellow_5)
|
||||
"gems" -> ContextCompat.getColor(context, R.color.green_10)
|
||||
"hourglasses" -> ContextCompat.getColor(context, R.color.brand_300)
|
||||
else -> 0
|
||||
}
|
||||
}
|
||||
val rightTextColor = when (item.currency) {
|
||||
"gold" -> ContextCompat.getColor(context, R.color.yellow_5)
|
||||
"gems" -> ContextCompat.getColor(context, R.color.green_10)
|
||||
"hourglasses" -> ContextCompat.getColor(context, R.color.brand_300)
|
||||
else -> 0
|
||||
}
|
||||
val a = (application?.currentActivity?.get() ?: getActivity() ?: ownerActivity)
|
||||
(a as? SnackbarActivity)?.showSnackbar(
|
||||
content = text,
|
||||
rightIcon = priceLabel.compoundDrawables[0],
|
||||
rightTextColor = rightTextColor,
|
||||
rightText = "-" + priceLabel.text,
|
||||
isCelebratory = true
|
||||
isCelebratory = true,
|
||||
)
|
||||
inventoryRepository.retrieveInAppRewards()
|
||||
userRepository.retrieveUser(forced = true)
|
||||
|
|
@ -497,9 +503,10 @@ class PurchaseDialog(
|
|||
val alert = HabiticaAlertDialog(context)
|
||||
alert.setTitle(R.string.excess_items)
|
||||
alert.setMessage(context.getString(R.string.excessItemsNoneLeft, item.text, purchaseQuantity, item.text))
|
||||
alert.addButton(context.getString(R.string.purchaseX, purchaseQuantity),
|
||||
alert.addButton(
|
||||
context.getString(R.string.purchaseX, purchaseQuantity),
|
||||
isPrimary = true,
|
||||
isDestructive = false
|
||||
isDestructive = false,
|
||||
) { _, _ ->
|
||||
buyItem(purchaseQuantity)
|
||||
}
|
||||
|
|
@ -516,12 +523,13 @@ class PurchaseDialog(
|
|||
shouldWarn = inventoryRepository.getPets(item.key, "quest", null).firstOrNull()?.isNotEmpty() ?: false
|
||||
ownedItems = inventoryRepository.getOwnedItems("eggs").firstOrNull()
|
||||
} else if (item.purchaseType == "hatchingPotions") {
|
||||
totalCount = if (item.path?.contains("wacky") == true) {
|
||||
// Wacky pets can't be raised to mounts, so only need half as many
|
||||
9
|
||||
} else {
|
||||
18
|
||||
}
|
||||
totalCount =
|
||||
if (item.path?.contains("wacky") == true) {
|
||||
// Wacky pets can't be raised to mounts, so only need half as many
|
||||
9
|
||||
} else {
|
||||
18
|
||||
}
|
||||
shouldWarn = inventoryRepository.getPets().firstOrNull()?.any { pet ->
|
||||
pet.animal == item.key && (pet.type == "premium" || pet.type == "wacky")
|
||||
} ?: false
|
||||
|
|
|
|||
|
|
@ -39,19 +39,23 @@ class PurchaseDialogCustomizationContent(context: Context) : PurchaseDialogConte
|
|||
}
|
||||
}
|
||||
|
||||
fun setAvatarWithPreview(user: User, shopItem: ShopItem) {
|
||||
fun setAvatarWithPreview(
|
||||
user: User,
|
||||
shopItem: ShopItem,
|
||||
) {
|
||||
val layerMap = EnumMap<AvatarView.LayerType, String>(AvatarView.LayerType::class.java)
|
||||
val path = shopItem.unlockPath ?: shopItem.path ?: ""
|
||||
val layerName = when {
|
||||
path.contains("skin") -> AvatarView.LayerType.SKIN
|
||||
path.contains("shirt") -> AvatarView.LayerType.SHIRT
|
||||
path.contains("color") -> AvatarView.LayerType.HAIR_BANGS
|
||||
path.contains("base") -> AvatarView.LayerType.HAIR_BASE
|
||||
path.contains("bangs") -> AvatarView.LayerType.HAIR_BANGS
|
||||
path.contains("beard") -> AvatarView.LayerType.HAIR_BEARD
|
||||
path.contains("mustache") -> AvatarView.LayerType.HAIR_MUSTACHE
|
||||
else -> null
|
||||
}
|
||||
val layerName =
|
||||
when {
|
||||
path.contains("skin") -> AvatarView.LayerType.SKIN
|
||||
path.contains("shirt") -> AvatarView.LayerType.SHIRT
|
||||
path.contains("color") -> AvatarView.LayerType.HAIR_BANGS
|
||||
path.contains("base") -> AvatarView.LayerType.HAIR_BASE
|
||||
path.contains("bangs") -> AvatarView.LayerType.HAIR_BANGS
|
||||
path.contains("beard") -> AvatarView.LayerType.HAIR_BEARD
|
||||
path.contains("mustache") -> AvatarView.LayerType.HAIR_MUSTACHE
|
||||
else -> null
|
||||
}
|
||||
layerName?.let {
|
||||
layerMap[it] = shopItem.imageName?.replace("shop_", "")?.replace("icon_", "")
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,30 +0,0 @@
|
|||
@file:OptIn(ExperimentalJsExport::class)
|
||||
|
||||
package com.habitrpg.shared.habitica
|
||||
|
||||
@JsExport
|
||||
actual class PlatformLogger actual constructor() {
|
||||
actual val enabled: Boolean
|
||||
get() = true
|
||||
|
||||
actual fun logDebug(tag: String, message: String) {
|
||||
console.log("[🥦] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logInfo(tag: String, message: String) {
|
||||
console.log("[🍋] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logWarning(tag: String, message: String) {
|
||||
console.log("[🍊] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logError(tag: String, message: String) {
|
||||
console.log("[🍎] $tag: $message")
|
||||
}
|
||||
|
||||
@JsName("logErrorException")
|
||||
actual fun logError(tag: String, message: String, exception: Throwable) {
|
||||
console.log("[🍎] $tag: $message\n${exception}")
|
||||
}
|
||||
}
|
||||
|
|
@ -5,4 +5,4 @@ actual class Platform actual constructor() {
|
|||
get() = "JS!"
|
||||
}
|
||||
|
||||
actual interface HParcelable
|
||||
actual interface HParcelable
|
||||
|
|
|
|||
|
|
@ -0,0 +1,46 @@
|
|||
@file:OptIn(ExperimentalJsExport::class)
|
||||
|
||||
package com.habitrpg.shared.habitica
|
||||
|
||||
@JsExport
|
||||
actual class PlatformLogger actual constructor() {
|
||||
actual val enabled: Boolean
|
||||
get() = true
|
||||
|
||||
actual fun logDebug(
|
||||
tag: String,
|
||||
message: String,
|
||||
) {
|
||||
console.log("[🥦] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logInfo(
|
||||
tag: String,
|
||||
message: String,
|
||||
) {
|
||||
console.log("[🍋] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logWarning(
|
||||
tag: String,
|
||||
message: String,
|
||||
) {
|
||||
console.log("[🍊] $tag: $message")
|
||||
}
|
||||
|
||||
actual fun logError(
|
||||
tag: String,
|
||||
message: String,
|
||||
) {
|
||||
console.log("[🍎] $tag: $message")
|
||||
}
|
||||
|
||||
@JsName("logErrorException")
|
||||
actual fun logError(
|
||||
tag: String,
|
||||
message: String,
|
||||
exception: Throwable,
|
||||
) {
|
||||
console.log("[🍎] $tag: $message\n$exception")
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue