mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-07-14 02:01:56 +00:00
commit
63bfbdff53
47 changed files with 409 additions and 274 deletions
|
|
@ -150,8 +150,8 @@ android {
|
|||
buildConfigField "String", "TESTING_LEVEL", "\"production\""
|
||||
resConfigs "en", "bg", "de", "en-rGB", "es", "fr", "hr-rHR", "in", "it", "iw", "ja", "ko", "lt", "nl", "pl", "pt-rBR", "pt-rPT", "ru", "tr", "zh", "zh-rTW"
|
||||
|
||||
versionCode 2847
|
||||
versionName "3.2.1"
|
||||
versionCode 2895
|
||||
versionName "3.2.2"
|
||||
}
|
||||
|
||||
buildFeatures {
|
||||
|
|
|
|||
|
|
@ -58,11 +58,13 @@
|
|||
<ImageView
|
||||
android:id="@+id/button_icon_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal"/>
|
||||
<TextView
|
||||
android:id="@+id/price_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_gravity="center_horizontal" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
<TextView
|
||||
|
|
|
|||
|
|
@ -5,14 +5,14 @@
|
|||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:background="?attr/colorContentBackground">
|
||||
<RelativeLayout
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:layout_centerVertical="true"
|
||||
android:paddingStart="@dimen/spacing_large"
|
||||
android:paddingEnd="@dimen/spacing_large"
|
||||
android:paddingBottom="20dp"
|
||||
|
|
@ -31,12 +31,11 @@
|
|||
</LinearLayout>
|
||||
<View
|
||||
android:id="@+id/rightBorderView"
|
||||
android:layout_width="5dp"
|
||||
android:layout_width="8dp"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true" />
|
||||
</RelativeLayout>
|
||||
android:background="@color/red_10"/>
|
||||
</LinearLayout>
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="0.5dp"
|
||||
|
|
|
|||
|
|
@ -344,7 +344,6 @@
|
|||
<string name="subscribe">Абониране</string>
|
||||
<string name="subscribe_listitem1_description">Александър Търговеца вече ще Ви продава диаманти на цената от 20 злато за диамант.\n\nМесечните му доставки отначало ще бъдат ограничени до 25 диаманта на месец, но може да се увеличат в зависимост от продължителността на абонамента Ви.\n\nОграничението се вдига с по 5 диаманта след всеки три месеца непрекъснат абонамент, докато не достигне 50 диаманта на месец!</string>
|
||||
<string name="subscribe_listitem2_description">Всеки месец ще получавате уникален козметичен предмет за героя си!\n\nОсвен това, за всеки три месеца непрекъснат абонамент, тайнствените пътешественици във времето ще Ви дават достъп до исторически козметични предмети (както и такива от бъдещето).</string>
|
||||
<string name="subscribe_listitem3_description">Прави завършените задачи и историята на задачите налични за по-дълго време.</string>
|
||||
<string name="subscribe_listitem4_description">С двойно увеличеното ограничение на паданията ще получавате повече предмети от завършените си задачи всеки ден, което ще Ви помогне да завършите конюшнята си по-бързо!</string>
|
||||
<string name="subscribe1month_gemcap">Ограничение от 25 диаманта</string>
|
||||
<string name="subscribe3month_gemcap">Ограничение от 30 диаманта</string>
|
||||
|
|
|
|||
|
|
@ -393,8 +393,8 @@
|
|||
<string name="empty_description_dailies">Dailies are tasks that repeat on a regular basis. Choose the schedule that works for you!</string>
|
||||
<string name="empty_title_todos">You don\'t have any To Do\'s</string>
|
||||
<string name="empty_description_todos">To Do\'s are tasks that only need to be completed once. Add checklists to your To Do\'s to increase their value.</string>
|
||||
<string name="empty_title_rewards">You don&rsquo;t have any Rewards</string>
|
||||
<string name="reset_walkthrough">Reset Justin&rsquo;s Walkthrough</string>
|
||||
<string name="empty_title_rewards">You don\'t have any Rewards</string>
|
||||
<string name="reset_walkthrough">Reset Justins Walkthrough</string>
|
||||
<string name="read_community_guidelines">Review our <u>Community Guidelines</u> before posting</string>
|
||||
<string name="maintenance">Maintenance</string>
|
||||
<string name="reload_content">Reload Content</string>
|
||||
|
|
|
|||
|
|
@ -935,8 +935,8 @@
|
|||
<string name="mount_category">%s Monturas</string>
|
||||
<string name="pet_category">%s Mascotas</string>
|
||||
<string name="challenge_task_name">%s Tareas del Desafío</string>
|
||||
<string name="delete_x_tasks">Eliminar %s Tareas</string>
|
||||
<string name="keep_x_tasks">Mantener %s Tareas</string>
|
||||
<string name="delete_x_tasks">Eliminar %d Tareas</string>
|
||||
<string name="keep_x_tasks">Mantener %d Tareas</string>
|
||||
<string name="broken_challenge">Desafío Terminado</string>
|
||||
<string name="pms_disabled_description">Tu puedes enviar mensajes todavía, pero nadie puede enviartelas a ti. Puedes habilitarlo de nuevo desde Configuraciones.</string>
|
||||
<string name="pms_disabled">Los Mensajes Privados están desactivados</string>
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="brand">@color/brand_400</color>
|
||||
<color name="color_accent">@color/brand_400</color>
|
||||
<color name="text_primary">@color/gray_600</color>
|
||||
<color name="text_secondary">@color/gray_500</color>
|
||||
|
|
@ -61,4 +62,4 @@
|
|||
<color name="text_brand_white">@color/white</color>
|
||||
<color name="lightly_tinted_background">@color/brand_50</color>
|
||||
<color name="dialog_background">@color/gray_10</color>
|
||||
</resources>
|
||||
</resources>
|
||||
|
|
|
|||
|
|
@ -942,7 +942,6 @@
|
|||
<string name="hatch_pet">Вырастить питомца</string>
|
||||
<string name="adventure_guide_description">Выполните эти задания и в итоге получите <b>5 достижений</b> и <font color=#EE9109><b>100 золота</b></font>!</string>
|
||||
<string name="pet_ownership_fraction">%1$d / %2$d</string>
|
||||
<string name="subscribe_listitem3_description_new">Подпишитесь сейчас, чтобы сразу же получить , а также получать новые предметы каждый месяц!</string>
|
||||
<string name="subscribe_listitem5">Удвойте количество выпадающих предметов</string>
|
||||
<string name="pms_disabled">Личные сообщения заблокированы</string>
|
||||
<string name="disablePrivateMessages">Заблокировать получение личных сообщений</string>
|
||||
|
|
|
|||
|
|
@ -994,7 +994,7 @@
|
|||
<string name="update_available">Güncelleme uygun: %1$s (%2$d)</string>
|
||||
<string name="streak">Seri</string>
|
||||
<string name="assigned_stat">Atanan İstatistik</string>
|
||||
<string name="join_party_description_guild">Aşağıdaki kullanıcı adını bir arkadaşınıza verin veya potansiyel yoldaşlarla tanışmak için % s gidin!</string>
|
||||
<string name="join_party_description_guild">Aşağıdaki kullanıcı adını bir arkadaşınıza verin veya potansiyel yoldaşlarla tanışmak için %s gidin!</string>
|
||||
<string name="spooky_promo_info_prompt">Değerli Taş Satışı, bu yılki Sonbahar Galasının en sonunda tekrar gündeme geldi! Bu, her zamankinden daha fazla Mücevher almak için son bir şans, bu yüzden bu süre boyunca stok yapın!</string>
|
||||
<string name="spooky_promo_info_limitations">Bu promosyon yalnızca sınırlı süreli etkinlik sırasında geçerlidir. Bu etkinlik 29 Ekim\'de 08:00 EDT\'de (12:00 UTC) başlayacak ve 2 Kasım 20:00 EDT\'de (00:00 UTC) sona erecek. Promosyon teklifi yalnızca kendiniz için Mücevher satın alırken geçerlidir.</string>
|
||||
<string name="spooky_promo_info_instructions">29 Ekim ile 2 Kasım arasında, her zamanki gibi herhangi bir Mücevher paketi satın alın ve hesabınıza promosyon miktarda Mücevher yatırılacaktır. Gelecek sürümler için harcayabileceğiniz, paylaşabileceğiniz veya saklayabileceğiniz daha fazla Mücevher!</string>
|
||||
|
|
|
|||
|
|
@ -339,7 +339,6 @@
|
|||
<string name="subscribe">加入會員</string>
|
||||
<string name="subscribe_listitem1_description">您將可以從市場上以每枚20金幣的價格購買寶石!</string>
|
||||
<string name="subscribe_listitem2_description">獲得神秘沙漏在時空穿越者的商店購買物品!</string>
|
||||
<string name="subscribe_listitem3_description">現在訂閱,可以得到贈品,和每月得到新的物品!</string>
|
||||
<string name="subscription_hourglasses">%d 個神秘沙漏</string>
|
||||
<string name="payment_method">付款方式</string>
|
||||
<string name="subscription">訂閱</string>
|
||||
|
|
@ -702,7 +701,7 @@
|
|||
<string name="cancel_subscription_google_description">不想再繼續捐助了?你可以在Google應用商店裡的“我的應用程序”部分找到退訂選項。</string>
|
||||
<string name="subscribe_listitem5_description">在Habitica發現更多的物品,每天享受雙倍的物品掉落上限。</string>
|
||||
<string name="subscribe_listitem4_description">如果您變成訂閱者,您可以獲得紫禦鹿角兔寵物。</string>
|
||||
<string name="subscribe_listitem3_description_new">現在訂閱,可以得到%@和每月得到新的物品!</string>
|
||||
<string name="subscribe_listitem3_description_new">現在訂閱,可以得到%s和每月得到新的物品!</string>
|
||||
<string name="subscribe_listitem5">雙倍掉落</string>
|
||||
<string name="create_account_short">創建一個帳戶</string>
|
||||
<string name="login_incentive_short_count">簽到%d次</string>
|
||||
|
|
|
|||
|
|
@ -344,7 +344,6 @@
|
|||
<string name="subscribe">订阅</string>
|
||||
<string name="subscribe_listitem1_description">您将可以从市场上以每枚20金币的价格购买宝石!</string>
|
||||
<string name="subscribe_listitem2_description">获得神秘沙漏在时空穿越者的商店购买物品!</string>
|
||||
<string name="subscribe_listitem3_description">现在订阅,可以得到赠品,和每月得到新的物品!</string>
|
||||
<string name="subscribe_listitem4_description">如果您变成订阅者,您可以获得紫御鹿角兔宠物。</string>
|
||||
<string name="subscribe1month_gemcap">25 宝石</string>
|
||||
<string name="subscribe3month_gemcap">30 宝石</string>
|
||||
|
|
@ -898,7 +897,7 @@
|
|||
<string name="register_btn_fb">Facebook登录</string>
|
||||
<string name="login_btn_apple">Apple登录</string>
|
||||
<string name="subscribe_listitem5_description">在Habitica发现更多的物品,每天享受双倍的物品掉落上限。</string>
|
||||
<string name="subscribe_listitem3_description_new">现在订阅,可以得到%@和每月得到新的物品!</string>
|
||||
<string name="subscribe_listitem3_description_new">现在订阅,可以得到%s和每月得到新的物品!</string>
|
||||
<string name="subscribe_listitem5">双倍掉落</string>
|
||||
<string name="create_account_short">创建一个帐户</string>
|
||||
<string name="login_incentive_short_count">签到%d次</string>
|
||||
|
|
|
|||
|
|
@ -12,4 +12,5 @@ interface ContentRepository {
|
|||
fun retrieveContent(context: Context?, forced: Boolean): Flowable<ContentResult>
|
||||
|
||||
fun retrieveWorldState(context: Context?): Flowable<WorldState>
|
||||
fun getWorldState(): Flowable<WorldState>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import androidx.preference.PreferenceManager
|
|||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.data.ContentRepository
|
||||
import com.habitrpg.android.habitica.data.local.ContentLocalRepository
|
||||
import com.habitrpg.android.habitica.helpers.AprilFoolsHandler
|
||||
import com.habitrpg.android.habitica.models.ContentResult
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.inventory.SpecialItem
|
||||
|
|
@ -43,16 +44,18 @@ abstract class ContentRepositoryImpl<T : ContentLocalRepository>(localRepository
|
|||
lastWorldStateSync = now
|
||||
apiClient.worldState.doOnNext {
|
||||
localRepository.saveWorldState(it)
|
||||
|
||||
val editor = PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
editor.putString("currentEvent", it.currentEventKey)
|
||||
editor.putString("currentEventPromo", it.currentEventPromo)
|
||||
editor.putLong("currentEventStartDate", it.currentEventStartDate?.time ?: 0)
|
||||
editor.putLong("currentEventEndDate", it.currentEventEndDate?.time ?: 0)
|
||||
editor.apply()
|
||||
for (event in it.events) {
|
||||
if (event.aprilFools != null && event.isCurrentlyActive) {
|
||||
AprilFoolsHandler.handle(event.aprilFools, event.end)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Flowable.empty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun getWorldState(): Flowable<WorldState> {
|
||||
return localRepository.getWorldState()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ import com.habitrpg.android.habitica.data.TaskRepository
|
|||
import com.habitrpg.android.habitica.data.UserRepository
|
||||
import com.habitrpg.android.habitica.data.local.UserLocalRepository
|
||||
import com.habitrpg.android.habitica.data.local.UserQuestStatus
|
||||
import com.habitrpg.android.habitica.extensions.skipNull
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.Achievement
|
||||
|
|
@ -38,16 +39,24 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
|
||||
override fun getUser(userID: String): Flowable<User> = localRepository.getUser(userID)
|
||||
|
||||
override fun updateUser(updateData: Map<String, Any>): Flowable<User> {
|
||||
private fun updateUser(userID: String, updateData: Map<String, Any>): Flowable<User> {
|
||||
return Flowable.zip(apiClient.updateUser(updateData),
|
||||
localRepository.getUser(userID).firstElement().toFlowable(),
|
||||
{ newUser, user -> mergeUser(user, newUser) })
|
||||
}
|
||||
|
||||
override fun updateUser(key: String, value: Any): Flowable<User> {
|
||||
private fun updateUser(userID: String, key: String, value: Any): Flowable<User> {
|
||||
val updateData = HashMap<String, Any>()
|
||||
updateData[key] = value
|
||||
return updateUser(updateData)
|
||||
return updateUser(userID, updateData)
|
||||
}
|
||||
|
||||
override fun updateUser(updateData: Map<String, Any>): Flowable<User> {
|
||||
return updateUser(userID, updateData)
|
||||
}
|
||||
|
||||
override fun updateUser(key: String, value: Any): Flowable<User> {
|
||||
return updateUser(userID, key, value)
|
||||
}
|
||||
|
||||
override fun retrieveUser(withTasks: Boolean): Flowable<User> =
|
||||
|
|
@ -75,7 +84,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
val timeZone = calendar.timeZone
|
||||
val offset = -TimeUnit.MINUTES.convert(timeZone.getOffset(calendar.timeInMillis).toLong(), TimeUnit.MILLISECONDS)
|
||||
if (offset.toInt() != user.preferences?.timezoneOffset ?: 0) {
|
||||
return@flatMap updateUser("preferences.timezoneOffset", offset.toString())
|
||||
return@flatMap updateUser(user.id ?: "", "preferences.timezoneOffset", offset.toString())
|
||||
} else {
|
||||
return@flatMap Flowable.just(user)
|
||||
}
|
||||
|
|
@ -164,7 +173,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
path = path.substring(1)
|
||||
return Flowable.zip(apiClient.unlockPath(path), localRepository.getUser(userID).firstElement().toFlowable()
|
||||
.map { localRepository.getUnmanagedCopy(it) }
|
||||
.skipNil(), { unlockResponse, copiedUser ->
|
||||
.skipNull(), { unlockResponse, copiedUser ->
|
||||
copiedUser.preferences = unlockResponse.preferences
|
||||
copiedUser.purchased = unlockResponse.purchased
|
||||
copiedUser.items = unlockResponse.items
|
||||
|
|
@ -380,7 +389,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
private fun getLiveUser(): Flowable<User> {
|
||||
return localRepository.getUser(userID)
|
||||
.map { localRepository.getLiveObject(it) }
|
||||
.skipNil()
|
||||
.skipNull()
|
||||
}
|
||||
|
||||
private fun <T> zipWithLiveUser(flowable: Flowable<T>, mergeFunc: BiFunction<T, User, T>): Flowable<T> {
|
||||
|
|
@ -424,8 +433,3 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
return copiedUser
|
||||
}
|
||||
}
|
||||
|
||||
private fun <T> Flowable<T?>.skipNil(): Flowable<T> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return skipWhile { it == null } as? Flowable<T> ?: Flowable.empty()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,8 +2,10 @@ package com.habitrpg.android.habitica.data.local
|
|||
|
||||
import com.habitrpg.android.habitica.models.ContentResult
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
|
||||
interface ContentLocalRepository : BaseLocalRepository {
|
||||
fun saveContent(contentResult: ContentResult)
|
||||
fun saveWorldState(worldState: WorldState)
|
||||
fun getWorldState(): Flowable<WorldState>
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,10 +1,13 @@
|
|||
package com.habitrpg.android.habitica.data.local.implementation
|
||||
|
||||
import com.habitrpg.android.habitica.data.local.ContentLocalRepository
|
||||
import com.habitrpg.android.habitica.extensions.skipNull
|
||||
import com.habitrpg.android.habitica.models.ContentResult
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.inventory.Quest
|
||||
import com.habitrpg.android.habitica.models.social.Group
|
||||
import hu.akarnokd.rxjava3.bridge.RxJavaBridge
|
||||
import io.reactivex.rxjava3.core.Flowable
|
||||
|
||||
import io.realm.Realm
|
||||
|
||||
|
|
@ -33,6 +36,15 @@ open class RealmContentLocalRepository(realm: Realm) : RealmBaseLocalRepository(
|
|||
}
|
||||
}
|
||||
|
||||
override fun getWorldState(): Flowable<WorldState> {
|
||||
return RxJavaBridge.toV3Flowable(realm.where(WorldState::class.java)
|
||||
.findAll()
|
||||
.asFlowable()
|
||||
.filter { it.isLoaded && it.size > 0 }
|
||||
.map { it.first() })
|
||||
.skipNull()
|
||||
}
|
||||
|
||||
override fun saveWorldState(worldState: WorldState) {
|
||||
val tavern = getUnmanagedCopy(realm.where(Group::class.java)
|
||||
.equalTo("id", Group.TAVERN_ID)
|
||||
|
|
@ -47,5 +59,6 @@ open class RealmContentLocalRepository(realm: Realm) : RealmBaseLocalRepository(
|
|||
tavern.quest?.key = worldState.worldBossKey
|
||||
tavern.quest?.progress = worldState.progress
|
||||
save(tavern)
|
||||
save(worldState)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,4 +7,9 @@ import io.reactivex.rxjava3.functions.Consumer
|
|||
|
||||
fun <T> Flowable<T>.subscribeWithErrorHandler(function: Consumer<T>): Disposable {
|
||||
return subscribe(function, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
fun <T> Flowable<T?>.skipNull(): Flowable<T> {
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return skipWhile { it == null } as? Flowable<T> ?: Flowable.empty()
|
||||
}
|
||||
|
|
@ -6,16 +6,26 @@ import com.google.firebase.remoteconfig.FirebaseRemoteConfig
|
|||
import com.google.gson.Gson
|
||||
import com.google.gson.reflect.TypeToken
|
||||
import com.habitrpg.android.habitica.BuildConfig
|
||||
import com.habitrpg.android.habitica.data.ContentRepository
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.promotions.HabiticaPromotion
|
||||
import com.habitrpg.android.habitica.models.promotions.getHabiticaPromotionFromKey
|
||||
import java.util.*
|
||||
|
||||
class AppConfigManager {
|
||||
class AppConfigManager(private val contentRepository: ContentRepository?) {
|
||||
|
||||
private var worldState: WorldState? = null
|
||||
|
||||
init {
|
||||
contentRepository?.getWorldState()?.subscribe( {
|
||||
worldState = it
|
||||
}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
|
||||
private val remoteConfig = FirebaseRemoteConfig.getInstance()
|
||||
|
||||
fun shopSpriteSuffix(): String {
|
||||
return remoteConfig.getString("shopSpriteSuffix")
|
||||
return worldState?.npcImageSuffix ?: remoteConfig.getString("shopSpriteSuffix")
|
||||
}
|
||||
|
||||
fun maxChatLength(): Long {
|
||||
|
|
@ -59,10 +69,6 @@ class AppConfigManager {
|
|||
return remoteConfig.getBoolean("enableLocalTaskScoring")
|
||||
}
|
||||
|
||||
fun flipAddTaskBehaviour(): Boolean {
|
||||
return remoteConfig.getBoolean("flipAddTaskBehaviour")
|
||||
}
|
||||
|
||||
fun insufficientGemPurchase(): Boolean {
|
||||
return remoteConfig.getBoolean("insufficientGemPurchase")
|
||||
}
|
||||
|
|
@ -100,15 +106,12 @@ class AppConfigManager {
|
|||
return remoteConfig.getBoolean("enableAdventureGuide")
|
||||
}
|
||||
|
||||
fun activePromo(context: Context): HabiticaPromotion? {
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
val key = preferences.getString("currentEvent", null)
|
||||
if (key?.isNotBlank() == true) {
|
||||
val startDateLong = preferences.getLong("currentEventStartDate", 0)
|
||||
val startDate = if (startDateLong > 0) Date(startDateLong) else null
|
||||
val endDateLong = preferences.getLong("currentEventEndDate", 0)
|
||||
val endDate = if (endDateLong > 0) Date(endDateLong) else null
|
||||
return getHabiticaPromotionFromKey(preferences.getString("currentEventPromo", null) ?: key, startDate, endDate)
|
||||
fun activePromo(): HabiticaPromotion? {
|
||||
for (event in worldState?.events ?: listOf(worldState?.currentEvent)) {
|
||||
if (event == null) return null
|
||||
if (event.promo != null) {
|
||||
return getHabiticaPromotionFromKey(event.promo ?: "", event.start, event.end)
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,32 @@
|
|||
package com.habitrpg.android.habitica.helpers
|
||||
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor
|
||||
import com.habitrpg.android.habitica.helpers.postProcessors.InvertPostProcessor
|
||||
import com.habitrpg.android.habitica.ui.AvatarView
|
||||
import java.util.*
|
||||
|
||||
class AprilFoolsHandler {
|
||||
|
||||
companion object {
|
||||
var eventEnd = Date(2020, 3, 1)
|
||||
|
||||
fun handle(name: String?, endDate: Date?) {
|
||||
if (endDate != null) {
|
||||
this.eventEnd = endDate
|
||||
}
|
||||
when(name) {
|
||||
"invert" -> invertFools()
|
||||
}
|
||||
}
|
||||
|
||||
private fun invertFools() {
|
||||
AvatarView.postProcessors[AvatarView.LayerType.PET] = {
|
||||
if (Date().after(eventEnd)) {
|
||||
null
|
||||
} else {
|
||||
InvertPostProcessor()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -17,16 +17,13 @@ class HabiticaFirebaseMessagingService : FirebaseMessagingService() {
|
|||
@Inject
|
||||
internal lateinit var pushNotificationManager: PushNotificationManager
|
||||
|
||||
@Inject
|
||||
internal lateinit var userRepository: UserRepository
|
||||
|
||||
override fun onMessageReceived(remoteMessage: RemoteMessage) {
|
||||
userComponent?.inject(this)
|
||||
if (this::pushNotificationManager.isInitialized) {
|
||||
pushNotificationManager.displayNotification(remoteMessage)
|
||||
|
||||
if (remoteMessage.data["identifier"]?.contains(PushNotificationManager.WON_CHALLENGE_PUSH_NOTIFICATION_KEY) == true) {
|
||||
userRepository.retrieveUser(true).subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
//userRepository.retrieveUser(true).subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,6 +32,9 @@ class PushNotificationManager(var apiClient: ApiClient, private val sharedPrefer
|
|||
|
||||
fun addPushDeviceUsingStoredToken() {
|
||||
FirebaseMessaging.getInstance().token.addOnCompleteListener {
|
||||
if (!it.isSuccessful) {
|
||||
return@addOnCompleteListener
|
||||
}
|
||||
this.refreshedToken = it.result
|
||||
if (this.refreshedToken.isEmpty() || this.user == null || this.userHasPushDevice() || !this.userIsSubscribedToNotifications()) {
|
||||
return@addOnCompleteListener
|
||||
|
|
|
|||
|
|
@ -0,0 +1,37 @@
|
|||
package com.habitrpg.android.habitica.helpers.postProcessors
|
||||
|
||||
import android.graphics.*
|
||||
import com.facebook.cache.common.CacheKey
|
||||
import com.facebook.cache.common.SimpleCacheKey
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor
|
||||
|
||||
|
||||
class InvertPostProcessor: BasePostprocessor() {
|
||||
|
||||
override fun getName(): String {
|
||||
return "invertPostProcessor"
|
||||
}
|
||||
|
||||
|
||||
override fun process(bitmap: Bitmap) {
|
||||
val canvas = Canvas(bitmap)
|
||||
val paint = Paint()
|
||||
|
||||
val matrixInvert = ColorMatrix()
|
||||
matrixInvert.set(floatArrayOf(
|
||||
-1.0f, 0.0f, 0.0f, 0.0f, 255.0f,
|
||||
0.0f, -1.0f, 0.0f, 0.0f, 255.0f,
|
||||
0.0f, 0.0f, -1.0f, 0.0f, 255.0f,
|
||||
0.0f, 0.0f, 0.0f, 1.0f, 0.0f
|
||||
))
|
||||
|
||||
val filter = ColorMatrixColorFilter(matrixInvert)
|
||||
paint.colorFilter = filter
|
||||
|
||||
canvas.drawBitmap(bitmap, 0f, 0f, paint)
|
||||
}
|
||||
|
||||
override fun getPostprocessorCacheKey(): CacheKey {
|
||||
return SimpleCacheKey("0")
|
||||
}
|
||||
}
|
||||
|
|
@ -1,18 +1,22 @@
|
|||
package com.habitrpg.android.habitica.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestProgress
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestRageStrike
|
||||
import io.realm.RealmList
|
||||
import io.realm.RealmModel
|
||||
import io.realm.RealmObject
|
||||
import java.util.*
|
||||
|
||||
class WorldState {
|
||||
|
||||
open class WorldState: RealmObject() {
|
||||
var worldBossKey: String = ""
|
||||
var worldBossActive: Boolean = false
|
||||
var progress: QuestProgress? = null
|
||||
var rageStrikes: MutableList<QuestRageStrike>? = null
|
||||
var rageStrikes: RealmList<QuestRageStrike>? = null
|
||||
|
||||
var currentEventKey: String? = null
|
||||
var currentEventPromo: String? = null
|
||||
var currentEventStartDate: Date? = null
|
||||
var currentEventEndDate: Date? = null
|
||||
var npcImageSuffix: String? = null
|
||||
|
||||
var currentEvent: WorldStateEvent? = null
|
||||
@SerializedName("currentEventList")
|
||||
var events: RealmList<WorldStateEvent> = RealmList()
|
||||
}
|
||||
|
|
@ -0,0 +1,31 @@
|
|||
package com.habitrpg.android.habitica.models
|
||||
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import io.realm.RealmModel
|
||||
import io.realm.RealmObject
|
||||
import io.realm.annotations.PrimaryKey
|
||||
import java.util.*
|
||||
|
||||
open class WorldStateEvent: RealmObject(), BaseObject {
|
||||
val isCurrentlyActive: Boolean
|
||||
get() {
|
||||
val now = Date()
|
||||
return (start?.before(now) == true) && (end?.after(now) == true)
|
||||
}
|
||||
|
||||
@PrimaryKey
|
||||
@SerializedName("event")
|
||||
var eventKey: String? = null
|
||||
var start: Date? = null
|
||||
var end: Date? = null
|
||||
var promo: String? = null
|
||||
var npcImageSuffix: String? = null
|
||||
var aprilFools: String? = null
|
||||
|
||||
override val realmClass: Class<out RealmModel>
|
||||
get() = WorldStateEvent::class.java
|
||||
override val primaryIdentifier: String?
|
||||
get() = eventKey
|
||||
override val primaryIdentifierName: String
|
||||
get() = "eventKey"
|
||||
}
|
||||
|
|
@ -1,48 +0,0 @@
|
|||
package com.habitrpg.android.habitica.models.auth;
|
||||
|
||||
/**
|
||||
* Created by magicmicky on 04/02/15.
|
||||
*/
|
||||
public class UserAuthResponse {
|
||||
//we need apiToken and token, as both are possible returns
|
||||
private String apiToken;
|
||||
private String token;
|
||||
private Boolean newUser = false;
|
||||
private String id;
|
||||
|
||||
public String getToken() {
|
||||
if (this.token == null) {
|
||||
return this.apiToken;
|
||||
} else {
|
||||
return this.token;
|
||||
}
|
||||
}
|
||||
|
||||
public void setToken(String token) {
|
||||
this.token = token;
|
||||
}
|
||||
|
||||
public String getApiToken() {
|
||||
return apiToken;
|
||||
}
|
||||
|
||||
public void setApiToken(String apiToken) {
|
||||
this.apiToken = apiToken;
|
||||
}
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public Boolean getNewUser() {
|
||||
return newUser;
|
||||
}
|
||||
|
||||
public void setNewUser(Boolean newUser) {
|
||||
this.newUser = newUser;
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
package com.habitrpg.android.habitica.models.auth
|
||||
|
||||
class UserAuthResponse {
|
||||
//we need apiToken and token, as both are possible returns
|
||||
var apiToken: String = ""
|
||||
var token: String
|
||||
get() {
|
||||
return apiToken
|
||||
}
|
||||
set(value) {
|
||||
apiToken = value
|
||||
}
|
||||
var newUser = false
|
||||
var id: String = ""
|
||||
}
|
||||
|
|
@ -8,6 +8,7 @@ import android.content.res.Resources;
|
|||
import androidx.preference.PreferenceManager;
|
||||
|
||||
import com.habitrpg.android.habitica.data.ApiClient;
|
||||
import com.habitrpg.android.habitica.data.ContentRepository;
|
||||
import com.habitrpg.android.habitica.executors.JobExecutor;
|
||||
import com.habitrpg.android.habitica.executors.PostExecutionThread;
|
||||
import com.habitrpg.android.habitica.executors.ThreadExecutor;
|
||||
|
|
@ -126,7 +127,7 @@ public class AppModule {
|
|||
|
||||
@Provides
|
||||
@Singleton
|
||||
AppConfigManager providesRemoteConfigManager() {
|
||||
return new AppConfigManager();
|
||||
AppConfigManager providesRemoteConfigManager(ContentRepository contentRepository) {
|
||||
return new AppConfigManager(contentRepository);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ import android.content.Context
|
|||
import android.graphics.*
|
||||
import android.graphics.drawable.Animatable
|
||||
import android.graphics.drawable.Drawable
|
||||
import android.net.Uri
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.util.Log
|
||||
|
|
@ -15,6 +16,8 @@ import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
|
|||
import com.facebook.drawee.view.DraweeHolder
|
||||
import com.facebook.drawee.view.MultiDraweeHolder
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.facebook.imagepipeline.request.BasePostprocessor
|
||||
import com.facebook.imagepipeline.request.ImageRequestBuilder
|
||||
import com.habitrpg.android.habitica.BuildConfig
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
|
|
@ -50,6 +53,15 @@ class AvatarView : View {
|
|||
return getLayerMap(avatar, true)
|
||||
}
|
||||
|
||||
private var spriteSubstitutions: Map<String, Map<String, String>> = HashMap()
|
||||
get() {
|
||||
if (Date().time - (lastSubstitutionCheck?.time ?: 0) > 180000) {
|
||||
field = AppConfigManager(null).spriteSubstitutions()
|
||||
lastSubstitutionCheck = Date()
|
||||
}
|
||||
return field
|
||||
}
|
||||
private var lastSubstitutionCheck: Date? = null
|
||||
|
||||
private val originalRect: Rect
|
||||
get() = if (showMount || showPet) FULL_HERO_RECT else if (showBackground) COMPACT_HERO_RECT else HERO_ONLY_RECT
|
||||
|
|
@ -122,8 +134,14 @@ class AvatarView : View {
|
|||
draweeHolder.topLevelDrawable?.callback = this
|
||||
multiDraweeHolder.add(draweeHolder)
|
||||
|
||||
val uri = Uri.parse(IMAGE_URI_ROOT + DataBindingUtils.getFullFilename(layerName, null))
|
||||
var request = ImageRequestBuilder.newBuilderWithSource(uri)
|
||||
postProcessors[layerKey]?.let {
|
||||
request = request.setPostprocessor(it())
|
||||
}
|
||||
|
||||
val controller = Fresco.newDraweeControllerBuilder()
|
||||
.setUri(IMAGE_URI_ROOT + DataBindingUtils.getFullFilename(layerName, null))
|
||||
.setImageRequest(request.build())
|
||||
.setControllerListener(object : BaseControllerListener<ImageInfo>() {
|
||||
override fun onFinalImageSet(
|
||||
id: String?,
|
||||
|
|
@ -151,8 +169,7 @@ class AvatarView : View {
|
|||
}
|
||||
|
||||
private fun getLayerMap(avatar: Avatar, resetHasAttributes: Boolean): Map<LayerType, String> {
|
||||
val substitutions = AppConfigManager().spriteSubstitutions()
|
||||
val layerMap = getAvatarLayerMap(avatar, substitutions)
|
||||
val layerMap = getAvatarLayerMap(avatar, spriteSubstitutions)
|
||||
|
||||
if (resetHasAttributes) {
|
||||
hasPet = false
|
||||
|
|
@ -162,7 +179,7 @@ class AvatarView : View {
|
|||
|
||||
var mountName = avatar.currentMount
|
||||
if (showMount && mountName?.isNotEmpty() == true) {
|
||||
mountName = substituteOrReturn(substitutions["mounts"], mountName)
|
||||
mountName = substituteOrReturn(spriteSubstitutions["mounts"], mountName)
|
||||
layerMap[LayerType.MOUNT_BODY] = "Mount_Body_$mountName"
|
||||
layerMap[LayerType.MOUNT_HEAD] = "Mount_Head_$mountName"
|
||||
if (resetHasAttributes) hasMount = true
|
||||
|
|
@ -170,14 +187,14 @@ class AvatarView : View {
|
|||
|
||||
var petName = avatar.currentPet
|
||||
if (showPet && petName?.isNotEmpty() == true) {
|
||||
petName = substituteOrReturn(substitutions["pets"], petName)
|
||||
petName = substituteOrReturn(spriteSubstitutions["pets"], petName)
|
||||
layerMap[LayerType.PET] = "Pet-$petName"
|
||||
if (resetHasAttributes) hasPet = true
|
||||
}
|
||||
|
||||
var backgroundName = avatar.preferences?.background
|
||||
if (showBackground && backgroundName?.isNotEmpty() == true) {
|
||||
backgroundName = substituteOrReturn(substitutions["backgrounds"], backgroundName)
|
||||
backgroundName = substituteOrReturn(spriteSubstitutions["backgrounds"], backgroundName)
|
||||
layerMap[LayerType.BACKGROUND] = "background_$backgroundName"
|
||||
if (resetHasAttributes) hasBackground = true
|
||||
}
|
||||
|
|
@ -243,6 +260,7 @@ class AvatarView : View {
|
|||
hasVisualBuffs = true
|
||||
}
|
||||
|
||||
val hair = prefs.hair
|
||||
if (!hasVisualBuffs) {
|
||||
if (!TextUtils.isEmpty(prefs.chair)) {
|
||||
layerMap[LayerType.CHAIR] = prefs.chair
|
||||
|
|
@ -279,7 +297,6 @@ class AvatarView : View {
|
|||
layerMap[LayerType.SHIRT] = prefs.size + "_shirt_" + prefs.shirt
|
||||
layerMap[LayerType.HEAD_0] = "head_0"
|
||||
|
||||
val hair = prefs.hair
|
||||
if (hair != null) {
|
||||
val hairColor = hair.color
|
||||
|
||||
|
|
@ -295,17 +312,11 @@ class AvatarView : View {
|
|||
if (hair.isAvailable(hair.beard)) {
|
||||
layerMap[LayerType.HAIR_BEARD] = "hair_beard_" + hair.beard + "_" + hairColor
|
||||
}
|
||||
if (hair.isAvailable(hair.flower)) {
|
||||
layerMap[LayerType.HAIR_FLOWER] = "hair_flower_" + hair.flower
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val hair = prefs.hair
|
||||
}
|
||||
|
||||
// Show flower all the time!
|
||||
if (hair != null && hair.isAvailable(hair.flower)) {
|
||||
layerMap[LayerType.HAIR_FLOWER] = "hair_flower_" + hair.flower
|
||||
}
|
||||
if (hair != null && hair.isAvailable(hair.flower)) {
|
||||
layerMap[LayerType.HAIR_FLOWER] = "hair_flower_" + hair.flower
|
||||
}
|
||||
|
||||
return layerMap
|
||||
|
|
@ -409,7 +420,7 @@ class AvatarView : View {
|
|||
|
||||
val equals = currentLayers != null && currentLayers == newLayerMap
|
||||
|
||||
if (!equals) {
|
||||
if (!equals || postProcessors.isNotEmpty()) {
|
||||
multiDraweeHolder.clear()
|
||||
numberLayersInProcess.set(0)
|
||||
}
|
||||
|
|
@ -521,5 +532,6 @@ class AvatarView : View {
|
|||
private val COMPACT_HERO_RECT = Rect(0, 0, 114, 114)
|
||||
private val HERO_ONLY_RECT = Rect(0, 0, 90, 90)
|
||||
|
||||
val postProcessors: MutableMap<LayerType, (() -> BasePostprocessor?)> = mutableMapOf()
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -371,7 +371,9 @@ class FullProfileActivity : BaseActivity() {
|
|||
|
||||
binding.equipmentTableLayout.removeAllViews()
|
||||
for (index in 1 until binding.attributesTableLayout.childCount) {
|
||||
binding.attributesTableLayout.removeViewAt(index)
|
||||
if (binding.attributesTableLayout != null) {
|
||||
binding.attributesTableLayout.removeViewAt(index)
|
||||
}
|
||||
}
|
||||
|
||||
addLevelAttributes(user)
|
||||
|
|
|
|||
|
|
@ -86,6 +86,12 @@ class GiftSubscriptionActivity : BaseActivity() {
|
|||
giftedUserID = it.id
|
||||
giftedUsername = it.username
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
|
||||
if (appConfigManager.activePromo()?.identifier == "g1g1") {
|
||||
binding.giftSubscriptionContainer.visibility = View.VISIBLE
|
||||
} else {
|
||||
binding.giftSubscriptionContainer.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
|
|
@ -198,7 +204,7 @@ class GiftSubscriptionActivity : BaseActivity() {
|
|||
}
|
||||
|
||||
private fun displayConfirmationDialog() {
|
||||
val message = getString(if (appConfigManager.activePromo(this)?.identifier == "g1g1"){
|
||||
val message = getString(if (appConfigManager.activePromo()?.identifier == "g1g1") {
|
||||
R.string.gift_confirmation_text_sub_g1g1
|
||||
} else {
|
||||
R.string.gift_confirmation_text_sub
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ class LoginActivity : BaseActivity(), Consumer<UserAuthResponse> {
|
|||
hideProgress()
|
||||
dismissKeyboard()
|
||||
try {
|
||||
saveTokens(userAuthResponse.token, userAuthResponse.id)
|
||||
saveTokens(userAuthResponse.apiToken, userAuthResponse.id)
|
||||
} catch (e: Exception) {
|
||||
crashlyticsProxy.logException(e)
|
||||
}
|
||||
|
|
@ -375,7 +375,7 @@ class LoginActivity : BaseActivity(), Consumer<UserAuthResponse> {
|
|||
FirebaseAnalytics.getInstance(this).logEvent("user_registered", null)
|
||||
}
|
||||
|
||||
compositeSubscription.add(userRepository.retrieveUser(true)
|
||||
compositeSubscription.add(userRepository.retrieveUser(true, true)
|
||||
.subscribe({
|
||||
if (userAuthResponse.newUser) {
|
||||
this.startSetupActivity()
|
||||
|
|
|
|||
|
|
@ -364,7 +364,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
|
||||
when (launchScreen) {
|
||||
"/party" -> {
|
||||
if (user?.party?.id != null) {
|
||||
if (user == null || user?.party?.id != null) {
|
||||
MainNavigationController.navigate(R.id.partyFragment)
|
||||
}
|
||||
}
|
||||
|
|
@ -634,17 +634,18 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction {
|
|||
|
||||
protected fun retrieveUser(forced: Boolean = false) {
|
||||
if (hostConfig.hasAuthentication()) {
|
||||
compositeSubscription.add(this.userRepository.retrieveUser(true, forced)
|
||||
.doOnNext { user1 ->
|
||||
FirebaseAnalytics.getInstance(this).setUserProperty("has_party", if (user1.party?.id?.isNotEmpty() == true) "true" else "false")
|
||||
FirebaseAnalytics.getInstance(this).setUserProperty("is_subscribed", if (user1.isSubscribed) "true" else "false")
|
||||
pushNotificationManager.setUser(user1)
|
||||
pushNotificationManager.addPushDeviceUsingStoredToken()
|
||||
}
|
||||
.flatMap { userRepository.retrieveTeamPlans() }
|
||||
.flatMap { contentRepository.retrieveContent(this,false) }
|
||||
.flatMap { contentRepository.retrieveWorldState(this) }
|
||||
.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
compositeSubscription.add(
|
||||
contentRepository.retrieveWorldState(this)
|
||||
.flatMap { userRepository.retrieveUser(true, forced) }
|
||||
.doOnNext { user1 ->
|
||||
FirebaseAnalytics.getInstance(this).setUserProperty("has_party", if (user1.party?.id?.isNotEmpty() == true) "true" else "false")
|
||||
FirebaseAnalytics.getInstance(this).setUserProperty("is_subscribed", if (user1.isSubscribed) "true" else "false")
|
||||
pushNotificationManager.setUser(user1)
|
||||
pushNotificationManager.addPushDeviceUsingStoredToken()
|
||||
}
|
||||
.flatMap { userRepository.retrieveTeamPlans() }
|
||||
.flatMap { contentRepository.retrieveContent(this) }
|
||||
.subscribe({ }, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ package com.habitrpg.android.habitica.ui.adapter
|
|||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.databinding.SkillTaskItemCardBinding
|
||||
|
|
@ -57,7 +58,7 @@ class SkillTasksRecyclerViewAdapter : BaseRecyclerViewAdapter<Task, SkillTasksRe
|
|||
binding.notesTextView.visibility = View.VISIBLE
|
||||
binding.notesTextView.text = task.markdownNotes { binding.notesTextView.text = it }
|
||||
}
|
||||
binding.rightBorderView.setBackgroundResource(task.lightTaskColor)
|
||||
binding.rightBorderView.setBackgroundColor(ContextCompat.getColor(itemView.context, task.lightTaskColor))
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
}
|
||||
|
||||
fun updatePromo() {
|
||||
activePromo = context?.let { configManager.activePromo(it) }
|
||||
activePromo = configManager.activePromo()
|
||||
val promoItem = getItemWithIdentifier(SIDEBAR_PROMO) ?: return
|
||||
if (activePromo != null) {
|
||||
promoItem.isVisible = true
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ class PromoInfoFragment : BaseMainFragment<FragmentPromoInfoBinding>() {
|
|||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
val promo = context?.let { configManager.activePromo(it) }
|
||||
val promo = configManager.activePromo()
|
||||
promo?.configureInfoFragment(this)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -164,6 +164,9 @@ open class ShopFragment : BaseMainFragment<FragmentRecyclerviewBinding>() {
|
|||
}
|
||||
if (shop1.identifier == Shop.TIME_TRAVELERS_SHOP) {
|
||||
formatTimeTravelersShop(shop1)
|
||||
} else if (shop1.identifier == Shop.SEASONAL_SHOP) {
|
||||
shop1.categories.reverse()
|
||||
shop1
|
||||
} else {
|
||||
shop1
|
||||
}
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ class GemsPurchaseFragment : BaseFragment<FragmentGemPurchaseBinding>(), GemPurc
|
|||
binding?.headerImageView?.setImageResource(R.drawable.gem_purchase_header_dark)
|
||||
}
|
||||
|
||||
val promo = context?.let { appConfigManager.activePromo(it) }
|
||||
val promo = appConfigManager.activePromo()
|
||||
if (promo != null) {
|
||||
binding?.let {
|
||||
promo.configurePurchaseBanner(it)
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class SubscriptionFragment : BaseFragment<FragmentSubscriptionBinding>(), GemPur
|
|||
binding?.subscriptionDetails?.visibility = View.GONE
|
||||
binding?.subscriptionDetails?.onShowSubscriptionOptions = { showSubscriptionOptions() }
|
||||
|
||||
binding?.giftSubscriptionButton?.setOnClickListener { context?.let { context -> showGiftSubscriptionDialog(context, appConfigManager.activePromo(context)?.identifier == "g1g1") } }
|
||||
binding?.giftSubscriptionButton?.setOnClickListener { context?.let { context -> showGiftSubscriptionDialog(context, appConfigManager.activePromo()?.identifier == "g1g1") } }
|
||||
|
||||
binding?.subscription1month?.setOnPurchaseClickListener { selectSubscription(PurchaseTypes.Subscription1Month) }
|
||||
binding?.subscription3month?.setOnPurchaseClickListener { selectSubscription(PurchaseTypes.Subscription3Month) }
|
||||
|
|
@ -86,7 +86,7 @@ class SubscriptionFragment : BaseFragment<FragmentSubscriptionBinding>(), GemPur
|
|||
|
||||
binding?.subscribeButton?.setOnClickListener { subscribeUser() }
|
||||
|
||||
val promo = context?.let { appConfigManager.activePromo(it) }
|
||||
val promo = appConfigManager.activePromo()
|
||||
if (promo != null) {
|
||||
binding?.let {
|
||||
promo.configurePurchaseBanner(it)
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ import com.habitrpg.android.habitica.R
|
|||
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
|
||||
import com.habitrpg.android.habitica.helpers.RxErrorHandler
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.SkillMemberActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.tasks.RewardsRecyclerViewAdapter
|
||||
|
|
@ -123,7 +122,7 @@ class RewardsRecyclerviewFragment : TaskRecyclerViewFragment() {
|
|||
fun newInstance(context: Context?, classType: String, showCustomRewards: Boolean): RewardsRecyclerviewFragment {
|
||||
val fragment = RewardsRecyclerviewFragment()
|
||||
fragment.retainInstance = true
|
||||
fragment.classType = classType
|
||||
fragment.taskType = classType
|
||||
fragment.showCustomRewards = showCustomRewards
|
||||
|
||||
if (context != null) {
|
||||
|
|
|
|||
|
|
@ -28,8 +28,6 @@ import com.habitrpg.android.habitica.helpers.TaskFilterHelper
|
|||
import com.habitrpg.android.habitica.models.responses.TaskDirection
|
||||
import com.habitrpg.android.habitica.models.responses.TaskScoringResult
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.activities.MainActivity
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.adapter.BaseRecyclerViewAdapter
|
||||
|
|
@ -41,10 +39,10 @@ import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
|||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.disposables.CompositeDisposable
|
||||
import java.util.*
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Named
|
||||
|
||||
open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBinding>(), androidx.swiperefreshlayout.widget.SwipeRefreshLayout.OnRefreshListener {
|
||||
internal var canEditTasks: Boolean = true
|
||||
|
|
@ -55,6 +53,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
return FragmentRefreshRecyclerviewBinding.inflate(inflater, container, false)
|
||||
}
|
||||
|
||||
protected var recyclerSubscription: CompositeDisposable = CompositeDisposable()
|
||||
var recyclerAdapter: TaskRecyclerViewAdapter? = null
|
||||
var itemAnimator = SafeDefaultItemAnimator()
|
||||
var ownerID: String = ""
|
||||
|
|
@ -75,29 +74,28 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
|
||||
internal var layoutManager: RecyclerView.LayoutManager? = null
|
||||
|
||||
internal var classType: String? = null
|
||||
internal var taskType: String = Task.TYPE_HABIT
|
||||
private var itemTouchCallback: ItemTouchHelper.Callback? = null
|
||||
|
||||
var refreshAction: ((() -> Unit) -> Unit)? = null
|
||||
|
||||
internal val className: String
|
||||
get() = this.classType ?: ""
|
||||
get() = this.taskType
|
||||
|
||||
// TODO needs a bit of cleanup
|
||||
private fun setInnerAdapter() {
|
||||
val adapter: BaseRecyclerViewAdapter<*, *>? = when (this.classType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
HabitsRecyclerViewAdapter(null, true, R.layout.habit_item_card, taskFilterHelper)
|
||||
}
|
||||
Task.TYPE_DAILY -> {
|
||||
DailiesRecyclerViewHolder(null, true, R.layout.daily_item_card, taskFilterHelper)
|
||||
}
|
||||
Task.TYPE_TODO -> {
|
||||
TodosRecyclerViewAdapter(null, true, R.layout.todo_item_card, taskFilterHelper)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
RewardsRecyclerViewAdapter(null, R.layout.reward_item_card)
|
||||
}
|
||||
if (binding?.recyclerView?.adapter != null && binding?.recyclerView?.adapter == recyclerAdapter && !recyclerSubscription.isDisposed) {
|
||||
return
|
||||
}
|
||||
if (!recyclerSubscription.isDisposed) {
|
||||
recyclerSubscription.dispose()
|
||||
}
|
||||
recyclerSubscription = CompositeDisposable()
|
||||
val adapter: BaseRecyclerViewAdapter<*, *>? = when (this.taskType) {
|
||||
Task.TYPE_HABIT -> HabitsRecyclerViewAdapter(null, true, R.layout.habit_item_card, taskFilterHelper)
|
||||
Task.TYPE_DAILY -> DailiesRecyclerViewHolder(null, true, R.layout.daily_item_card, taskFilterHelper)
|
||||
Task.TYPE_TODO -> TodosRecyclerViewAdapter(null, true, R.layout.todo_item_card, taskFilterHelper)
|
||||
Task.TYPE_REWARD -> RewardsRecyclerViewAdapter(null, R.layout.reward_item_card)
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
|
@ -106,10 +104,29 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
binding?.recyclerView?.adapter = adapter
|
||||
|
||||
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
|
||||
|
||||
recyclerAdapter?.errorButtonEvents?.subscribe({
|
||||
taskRepository.syncErroredTasks().subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
}, RxErrorHandler.handleEmptyError())?.let { recyclerSubscription.add(it) }
|
||||
recyclerAdapter?.taskOpenEvents?.subscribeWithErrorHandler {
|
||||
openTaskForm(it)
|
||||
}?.let { recyclerSubscription.add(it) }
|
||||
recyclerAdapter?.taskScoreEvents
|
||||
?.doOnNext { playSound(it.second) }
|
||||
?.subscribeWithErrorHandler { scoreTask(it.first, it.second) }?.let { recyclerSubscription.add(it) }
|
||||
recyclerAdapter?.checklistItemScoreEvents
|
||||
?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "")
|
||||
}?.subscribeWithErrorHandler {}?.let { recyclerSubscription.add(it) }
|
||||
recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { recyclerSubscription.add(it) }
|
||||
|
||||
recyclerSubscription.add(taskRepository.getTasks(this.taskType, ownerID).subscribe({
|
||||
this.recyclerAdapter?.updateUnfilteredData(it)
|
||||
this.recyclerAdapter?.filter()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
private fun handleTaskResult(result: TaskScoringResult, value: Int) {
|
||||
if (classType == Task.TYPE_REWARD) {
|
||||
if (taskType == Task.TYPE_REWARD) {
|
||||
(activity as? MainActivity)?.let { activity ->
|
||||
HabiticaSnackbar.showSnackbar(activity.snackbarContainer, null, getString(R.string.notification_purchase_reward),
|
||||
BitmapDrawable(resources, HabiticaIconsHelper.imageOfGold()),
|
||||
|
|
@ -123,7 +140,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
|
||||
private fun playSound(direction: TaskDirection) {
|
||||
val soundName = when (classType) {
|
||||
val soundName = when (taskType) {
|
||||
Task.TYPE_HABIT -> if (direction == TaskDirection.UP) SoundManager.SoundPlusHabit else SoundManager.SoundMinusHabit
|
||||
Task.TYPE_DAILY -> SoundManager.SoundDaily
|
||||
Task.TYPE_TODO -> SoundManager.SoundTodo
|
||||
|
|
@ -160,18 +177,20 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
context?.let { binding?.recyclerView?.setBackgroundColor(ContextCompat.getColor(it, R.color.content_background)) }
|
||||
savedInstanceState?.let { this.classType = savedInstanceState.getString(CLASS_TYPE_KEY, "") }
|
||||
savedInstanceState?.let { this.taskType = savedInstanceState.getString(CLASS_TYPE_KEY, "") }
|
||||
|
||||
when (classType) {
|
||||
when (taskType) {
|
||||
Task.TYPE_TODO -> taskFilterHelper.setActiveFilter(Task.TYPE_TODO, Task.FILTER_ACTIVE)
|
||||
Task.TYPE_DAILY -> {
|
||||
val user = (activity as? MainActivity)?.user
|
||||
if (user?.isValid == true && user?.preferences?.dailyDueDefaultView == true) {
|
||||
if (user?.isValid == true && user.preferences?.dailyDueDefaultView == true) {
|
||||
taskFilterHelper.setActiveFilter(Task.TYPE_DAILY, Task.FILTER_ACTIVE)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
recyclerAdapter?.filter()
|
||||
|
||||
itemTouchCallback = object : ItemTouchHelper.Callback() {
|
||||
override fun onSelectedChanged(viewHolder: RecyclerView.ViewHolder?, actionState: Int) {
|
||||
super.onSelectedChanged(viewHolder, actionState)
|
||||
|
|
@ -221,7 +240,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
if (validTaskId != null) {
|
||||
recyclerAdapter?.ignoreUpdates = true
|
||||
compositeSubscription.add(taskRepository.updateTaskPosition(
|
||||
classType ?: "", validTaskId, viewHolder.adapterPosition
|
||||
taskType, validTaskId, viewHolder.adapterPosition
|
||||
)
|
||||
.delay(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
|
|
@ -233,40 +252,15 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
|
||||
binding?.recyclerView?.setScaledPadding(context, 0, 0, 0, 48)
|
||||
binding?.recyclerView?.adapter = recyclerAdapter as? RecyclerView.Adapter<*>
|
||||
recyclerAdapter?.filter()
|
||||
|
||||
layoutManager = getLayoutManager(context)
|
||||
layoutManager?.isItemPrefetchEnabled = false
|
||||
binding?.recyclerView?.layoutManager = layoutManager
|
||||
|
||||
if (binding?.recyclerView?.adapter == null) {
|
||||
this.setInnerAdapter()
|
||||
}
|
||||
this.setInnerAdapter()
|
||||
|
||||
allowReordering()
|
||||
|
||||
if (this.classType != null) {
|
||||
recyclerAdapter?.errorButtonEvents?.subscribe({
|
||||
taskRepository.syncErroredTasks().subscribe({}, RxErrorHandler.handleEmptyError())
|
||||
}, RxErrorHandler.handleEmptyError())?.let { compositeSubscription.add(it) }
|
||||
recyclerAdapter?.taskOpenEvents?.subscribeWithErrorHandler {
|
||||
openTaskForm(it)
|
||||
}?.let { compositeSubscription.add(it) }
|
||||
recyclerAdapter?.taskScoreEvents
|
||||
?.doOnNext { playSound(it.second) }
|
||||
?.subscribeWithErrorHandler { scoreTask(it.first, it.second) }?.let { compositeSubscription.add(it) }
|
||||
recyclerAdapter?.checklistItemScoreEvents
|
||||
?.flatMap { taskRepository.scoreChecklistItem(it.first.id ?: "", it.second.id ?: "")
|
||||
}?.subscribeWithErrorHandler {}?.let { compositeSubscription.add(it) }
|
||||
recyclerAdapter?.brokenTaskEvents?.subscribeWithErrorHandler { showBrokenChallengeDialog(it) }?.let { compositeSubscription.add(it) }
|
||||
|
||||
compositeSubscription.add(taskRepository.getTasks(this.classType ?: "", ownerID).subscribe({
|
||||
this.recyclerAdapter?.updateUnfilteredData(it)
|
||||
this.recyclerAdapter?.filter()
|
||||
}, RxErrorHandler.handleEmptyError()))
|
||||
}
|
||||
|
||||
val bottomPadding = ((binding?.recyclerView?.paddingBottom ?: 0) + TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 60f, resources.displayMetrics)).toInt()
|
||||
binding?.recyclerView?.setPadding(0, 0, 0, bottomPadding)
|
||||
binding?.recyclerView?.itemAnimator = itemAnimator
|
||||
|
|
@ -319,52 +313,50 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
|
||||
private fun setEmptyLabels() {
|
||||
if (this.classType != null) {
|
||||
binding?.recyclerView?.setEmptyView(binding?.emptyView)
|
||||
context?.let { binding?.emptyIconView?.setColorFilter(ContextCompat.getColor(it, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY) }
|
||||
if (taskFilterHelper.howMany(classType) > 0) {
|
||||
when (this.classType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_habits_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_habits_filtered)
|
||||
}
|
||||
Task.TYPE_DAILY -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies_filtered)
|
||||
}
|
||||
Task.TYPE_TODO -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_todos_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_todos_filtered)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
|
||||
}
|
||||
binding?.recyclerView?.setEmptyView(binding?.emptyView)
|
||||
context?.let { binding?.emptyIconView?.setColorFilter(ContextCompat.getColor(it, R.color.text_dimmed), android.graphics.PorterDuff.Mode.MULTIPLY) }
|
||||
if (taskFilterHelper.howMany(taskType) > 0) {
|
||||
when (this.taskType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_habits_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_habits_filtered)
|
||||
}
|
||||
} else {
|
||||
when (this.classType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_habits)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_habits)
|
||||
}
|
||||
Task.TYPE_DAILY -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies)
|
||||
}
|
||||
Task.TYPE_TODO -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_todos)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_todos)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
|
||||
}
|
||||
Task.TYPE_DAILY -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies_filtered)
|
||||
}
|
||||
Task.TYPE_TODO -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_todos_filtered)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_todos_filtered)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
when (this.taskType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_habits)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_habits)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_habits)
|
||||
}
|
||||
Task.TYPE_DAILY -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_dailies)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_dailies)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_dailies)
|
||||
}
|
||||
Task.TYPE_TODO -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_todos)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_todos)
|
||||
binding?.emptyViewDescription?.setText(R.string.empty_description_todos)
|
||||
}
|
||||
Task.TYPE_REWARD -> {
|
||||
binding?.emptyIconView?.setImageResource(R.drawable.icon_rewards)
|
||||
binding?.emptyViewTitle?.setText(R.string.empty_title_rewards)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -378,11 +370,11 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
super.onSaveInstanceState(outState)
|
||||
outState.putString(CLASS_TYPE_KEY, this.classType)
|
||||
outState.putString(CLASS_TYPE_KEY, this.taskType)
|
||||
}
|
||||
|
||||
override val displayedClassName: String?
|
||||
get() = this.classType + super.displayedClassName
|
||||
get() = this.taskType + super.displayedClassName
|
||||
|
||||
override fun onRefresh() {
|
||||
binding?.refreshLayout?.isRefreshing = true
|
||||
|
|
@ -391,13 +383,22 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (binding?.recyclerView?.adapter == null || recyclerAdapter == null) {
|
||||
setInnerAdapter()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
context?.let { recyclerAdapter?.taskDisplayMode = configManager.taskDisplayMode(it) }
|
||||
setInnerAdapter()
|
||||
recyclerAdapter?.filter()
|
||||
}
|
||||
|
||||
fun setActiveFilter(activeFilter: String) {
|
||||
taskFilterHelper.setActiveFilter(classType ?: "", activeFilter)
|
||||
taskFilterHelper.setActiveFilter(taskType, activeFilter)
|
||||
recyclerAdapter?.filter()
|
||||
|
||||
setEmptyLabels()
|
||||
|
|
@ -431,10 +432,10 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
|
|||
fun newInstance(context: Context?, classType: String): TaskRecyclerViewFragment {
|
||||
val fragment = TaskRecyclerViewFragment()
|
||||
fragment.retainInstance = true
|
||||
fragment.classType = classType
|
||||
fragment.taskType = classType
|
||||
var tutorialTexts: List<String>? = null
|
||||
if (context != null) {
|
||||
when (fragment.classType) {
|
||||
when (fragment.taskType) {
|
||||
Task.TYPE_HABIT -> {
|
||||
fragment.tutorialStepIdentifier = "habits"
|
||||
tutorialTexts = listOf(context.getString(R.string.tutorial_overview), context.getString(R.string.tutorial_habits_1), context.getString(R.string.tutorial_habits_2), context.getString(R.string.tutorial_habits_3), context.getString(R.string.tutorial_habits_4))
|
||||
|
|
|
|||
|
|
@ -18,7 +18,6 @@ import com.habitrpg.android.habitica.extensions.getThemeColor
|
|||
import com.habitrpg.android.habitica.extensions.setTintWith
|
||||
import com.habitrpg.android.habitica.helpers.*
|
||||
import com.habitrpg.android.habitica.models.tasks.Task
|
||||
import com.habitrpg.android.habitica.models.user.User
|
||||
import com.habitrpg.android.habitica.modules.AppModule
|
||||
import com.habitrpg.android.habitica.ui.activities.TaskFormActivity
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
|
|
@ -178,7 +177,7 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
disposable = tagRepository.getTags().subscribe({ tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
|
||||
dialog.setActiveTags(taskFilterHelper.tags)
|
||||
if (activeFragment != null) {
|
||||
val taskType = activeFragment?.classType
|
||||
val taskType = activeFragment?.taskType
|
||||
if (taskType != null) {
|
||||
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
|
||||
}
|
||||
|
|
@ -257,9 +256,9 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
}
|
||||
|
||||
private fun updateFilterIcon() {
|
||||
val filterCount = taskFilterHelper.howMany(activeFragment?.classType)
|
||||
val filterCount = taskFilterHelper.howMany(activeFragment?.taskType)
|
||||
|
||||
filterMenuItem?.isVisible = activeFragment?.classType != Task.TYPE_REWARD
|
||||
filterMenuItem?.isVisible = activeFragment?.taskType != Task.TYPE_REWARD
|
||||
if (filterCount == 0) {
|
||||
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)
|
||||
context?.let {
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchVi
|
|||
disposable = tagRepository.getTags().subscribe({ tagsList -> dialog.setTags(tagsList)}, RxErrorHandler.handleEmptyError())
|
||||
dialog.setActiveTags(taskFilterHelper.tags)
|
||||
if (activeFragment != null) {
|
||||
val taskType = activeFragment?.classType
|
||||
val taskType = activeFragment?.taskType
|
||||
if (taskType != null) {
|
||||
dialog.setTaskType(taskType, taskFilterHelper.getActiveFilter(taskType))
|
||||
}
|
||||
|
|
@ -265,7 +265,7 @@ class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchVi
|
|||
}
|
||||
var filterCount = 0
|
||||
if (activeFragment != null) {
|
||||
filterCount = taskFilterHelper.howMany(activeFragment?.classType)
|
||||
filterCount = taskFilterHelper.howMany(activeFragment?.taskType)
|
||||
}
|
||||
if (filterCount == 0) {
|
||||
filterMenuItem?.setIcon(R.drawable.ic_action_filter_list)
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ object DataBindingUtils {
|
|||
private var spriteSubstitutions: Map<String, String> = HashMap()
|
||||
get() {
|
||||
if (Date().time - (lastSubstitutionCheck?.time ?: 0) > 180000) {
|
||||
field = AppConfigManager().spriteSubstitutions().get("generic") ?: HashMap()
|
||||
field = AppConfigManager(null).spriteSubstitutions()["generic"] ?: HashMap()
|
||||
lastSubstitutionCheck = Date()
|
||||
}
|
||||
return field
|
||||
|
|
|
|||
|
|
@ -67,7 +67,9 @@ class ChallengeDeserializer : JsonDeserializer<Challenge>, JsonSerializer<Challe
|
|||
challenge.updatedAt = context.deserialize(jsonObject.get("updatedAt"), Date::class.java)
|
||||
}
|
||||
|
||||
challenge.summary = jsonObject.get("summary").asString
|
||||
if (jsonObject.has("summary")) {
|
||||
challenge.summary = jsonObject.get("summary").asString
|
||||
}
|
||||
|
||||
val groupElement = jsonObject.get("group")
|
||||
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import com.google.gson.JsonDeserializer
|
|||
import com.google.gson.JsonElement
|
||||
import com.habitrpg.android.habitica.extensions.getAsString
|
||||
import com.habitrpg.android.habitica.models.WorldState
|
||||
import com.habitrpg.android.habitica.models.WorldStateEvent
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestProgress
|
||||
import com.habitrpg.android.habitica.models.inventory.QuestRageStrike
|
||||
import io.realm.RealmList
|
||||
import java.lang.reflect.Type
|
||||
import java.util.*
|
||||
|
||||
|
|
@ -37,7 +39,7 @@ class WorldStateSerialization: JsonDeserializer<WorldState> {
|
|||
val extra = worldBossObject["extra"].asJsonObject
|
||||
if (extra.has("worldDmg")) {
|
||||
val worldDmg = extra["worldDmg"].asJsonObject
|
||||
state.rageStrikes = mutableListOf()
|
||||
state.rageStrikes = RealmList()
|
||||
worldDmg.entrySet().forEach { (key, value) ->
|
||||
val strike = QuestRageStrike(key, value.asBoolean)
|
||||
state.rageStrikes?.add(strike)
|
||||
|
|
@ -46,14 +48,20 @@ class WorldStateSerialization: JsonDeserializer<WorldState> {
|
|||
}
|
||||
}
|
||||
|
||||
state.npcImageSuffix = json?.asJsonObject.getAsString("npcImageSuffix")
|
||||
|
||||
try {
|
||||
if (json?.asJsonObject?.has("currentEvent") == true && json.asJsonObject?.get("currentEvent")?.isJsonObject == true) {
|
||||
val event = json.asJsonObject?.getAsJsonObject("currentEvent")
|
||||
if (event != null) {
|
||||
state.currentEventKey = event.getAsString("event")
|
||||
state.currentEventPromo = if (event.has("promo")) event.getAsString("promo") else null
|
||||
state.currentEventStartDate = context?.deserialize(event.get("start"), Date::class.java)
|
||||
state.currentEventEndDate = context?.deserialize(event.get("end"), Date::class.java)
|
||||
state.currentEvent = context?.deserialize(event, WorldStateEvent::class.java)
|
||||
}
|
||||
if (json.asJsonObject.has("currentEventList")) {
|
||||
val events = RealmList<WorldStateEvent>()
|
||||
for (element in json.asJsonObject.getAsJsonArray("currentEventList")) {
|
||||
context?.deserialize<WorldStateEvent>(element, WorldStateEvent::class.java)?.let { events.add(it) }
|
||||
}
|
||||
state.events = events
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
|
|
|
|||
|
|
@ -14,12 +14,12 @@ buildscript {
|
|||
classpath 'com.android.tools.build:gradle:4.1.2'
|
||||
classpath 'com.neenbedankt.gradle.plugins:android-apt:1.8'
|
||||
classpath 'com.google.gms:google-services:4.3.5'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.0'
|
||||
classpath 'com.google.firebase:firebase-crashlytics-gradle:2.5.1'
|
||||
classpath "io.realm:realm-gradle-plugin:10.3.0"
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
classpath "io.gitlab.arturbosch.detekt:detekt-gradle-plugin:1.1.0"
|
||||
classpath "android.arch.navigation:navigation-safe-args-gradle-plugin:1.0.0"
|
||||
classpath 'com.google.firebase:perf-plugin:1.3.4'
|
||||
classpath 'com.google.firebase:perf-plugin:1.3.5'
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1 +1 @@
|
|||
In this update we've cleaned up Guilds and Challenges to look better than ever. Check them out and maybe find something new! Starting or leaving a Quest will update the Party view right away. Snowballs will actually transform your party members now. Certain notifications work better, bringing you to the right place and no longer crashing the app in some cases. Subscriber Mystery Boxes have a fancy new view when opening as well. Check these out and lots of other fixes by downloading the update!
|
||||
We’ve added the ability to select different default launch screens! Do you mainly use Dailies or find yourself constantly checking off To Do’s? Now you can set the app to automatically launch to either of those screens, and more! Go to Settings to explore the various launch screen options. We’ve also fixed a few bugs with views not updating automatically. Hope you enjoy this newest Habitica update!
|
||||
|
|
|
|||
Loading…
Reference in a new issue