mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-04-14 11:46:32 +00:00
fix crashes
This commit is contained in:
parent
10820923a1
commit
5a61b26ea1
18 changed files with 81 additions and 45 deletions
|
|
@ -16,7 +16,7 @@
|
|||
|
||||
<application
|
||||
android:name=".HabiticaApplication"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:roundIcon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
|
||||
<application
|
||||
android:name=".HabiticaApplication"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:theme="@style/AppTheme">
|
||||
|
|
|
|||
|
|
@ -111,7 +111,7 @@ dependencies {
|
|||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutines_version"
|
||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutines_version"
|
||||
implementation "com.google.accompanist:accompanist-themeadapter-material:$accompanist_version"
|
||||
implementation "androidx.compose.material3:material3:1.1.2"
|
||||
implementation "androidx.compose.material3:material3:1.2.0"
|
||||
implementation "com.google.accompanist:accompanist-systemuicontroller:$accompanist_version"
|
||||
implementation 'com.google.android.play:core:1.10.3'
|
||||
|
||||
|
|
@ -127,6 +127,8 @@ dependencies {
|
|||
implementation project(':shared')
|
||||
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
|
||||
|
||||
implementation 'com.gu.android:toolargetool:0.3.0'
|
||||
}
|
||||
|
||||
android {
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import com.google.android.gms.wearable.Wearable
|
|||
import com.google.firebase.installations.FirebaseInstallations
|
||||
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
|
||||
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
|
||||
import com.gu.toolargetool.TooLargeTool
|
||||
import com.habitrpg.android.habitica.data.ApiClient
|
||||
import com.habitrpg.android.habitica.extensions.DateUtils
|
||||
import com.habitrpg.android.habitica.helpers.AdHandler
|
||||
|
|
@ -113,10 +114,12 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
|
|||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
lifecycleTracker = ApplicationLifecycleTracker(sharedPrefs)
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(lifecycleTracker)
|
||||
|
||||
if (!BuildConfig.DEBUG) {
|
||||
TooLargeTool.startLogging(this)
|
||||
try {
|
||||
Analytics.initialize(this)
|
||||
} catch (ignored: Resources.NotFoundException) {
|
||||
|
|
|
|||
|
|
@ -17,5 +17,9 @@ fun Resources.forceLocale(activity: BaseActivity, locale: Locale) {
|
|||
}
|
||||
updateConfiguration(configuration, displayMetrics)
|
||||
|
||||
Firebase.crashlytics.setCustomKey("language", locale.toLanguageTag())
|
||||
try {
|
||||
Firebase.crashlytics.setCustomKey("language", locale.toLanguageTag())
|
||||
} catch (_: IllegalStateException) {
|
||||
// issue with getting firebase
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -119,11 +119,13 @@ class AppConfigManager(contentRepository: ContentRepository?) : com.habitrpg.com
|
|||
|
||||
fun activePromo(): HabiticaPromotion? {
|
||||
var promo: HabiticaPromotion? = null
|
||||
for (event in worldState?.events ?: listOf(worldState?.currentEvent)) {
|
||||
if (event == null) return null
|
||||
val thisPromo = getHabiticaPromotionFromKey(event.promo ?: event.eventKey ?: "", event.start, event.end)
|
||||
if (thisPromo != null) {
|
||||
promo = thisPromo
|
||||
if (worldState?.isValid == true) {
|
||||
for (event in worldState?.events ?: listOf(worldState?.currentEvent)) {
|
||||
if (event == null) return null
|
||||
val thisPromo = getHabiticaPromotionFromKey(event.promo ?: event.eventKey ?: "", event.start, event.end)
|
||||
if (thisPromo != null) {
|
||||
promo = thisPromo
|
||||
}
|
||||
}
|
||||
}
|
||||
if (promo == null && remoteConfig.getString("activePromo").isNotBlank()) {
|
||||
|
|
|
|||
|
|
@ -51,6 +51,7 @@ open class Customization : RealmObject(), BaseObject {
|
|||
}
|
||||
|
||||
fun getImageName(userSize: String?, hairColor: String?): String? {
|
||||
if (!this.isValid()) { return null }
|
||||
if (identifier?.isNotBlank() != true || identifier == "none" || identifier == "0") return null
|
||||
return when (type) {
|
||||
"skin" -> return "skin_$identifier"
|
||||
|
|
|
|||
|
|
@ -9,13 +9,13 @@ import com.habitrpg.android.habitica.R
|
|||
import com.habitrpg.android.habitica.databinding.CustomizationGridItemBinding
|
||||
import com.habitrpg.android.habitica.databinding.CustomizationSectionFooterBinding
|
||||
import com.habitrpg.android.habitica.databinding.CustomizationSectionHeaderBinding
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.inventory.Customization
|
||||
import com.habitrpg.android.habitica.models.inventory.CustomizationSet
|
||||
import com.habitrpg.android.habitica.models.shops.ShopItem
|
||||
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
|
||||
import com.habitrpg.common.habitica.extensions.dpToPx
|
||||
import com.habitrpg.common.habitica.extensions.loadImage
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.common.habitica.views.AvatarView
|
||||
import com.habitrpg.shared.habitica.models.Avatar
|
||||
import java.util.Date
|
||||
|
|
@ -198,6 +198,9 @@ class CustomizationRecyclerViewAdapter : androidx.recyclerview.widget.RecyclerVi
|
|||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
if (customization?.isValid != true) {
|
||||
return
|
||||
}
|
||||
if (customization?.isUsable(ownedCustomizations.contains(customization?.id)) == false) {
|
||||
if (customization?.customizationSet?.contains("timeTravel") == true) {
|
||||
val dialog = HabiticaAlertDialog(itemView.context)
|
||||
|
|
|
|||
|
|
@ -30,7 +30,6 @@ import com.habitrpg.android.habitica.extensions.getMinuteOrSeconds
|
|||
import com.habitrpg.android.habitica.extensions.getRemainingString
|
||||
import com.habitrpg.android.habitica.extensions.getShortRemainingString
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.models.WorldStateEvent
|
||||
import com.habitrpg.android.habitica.models.inventory.Item
|
||||
import com.habitrpg.android.habitica.models.promotions.HabiticaPromotion
|
||||
|
|
@ -45,8 +44,10 @@ import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
|||
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
|
||||
import com.habitrpg.common.habitica.extensions.getThemeColor
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import io.realm.kotlin.isValid
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
|
|
@ -58,7 +59,6 @@ import java.util.concurrent.TimeUnit
|
|||
import javax.inject.Inject
|
||||
import kotlin.time.Duration
|
||||
import kotlin.time.DurationUnit
|
||||
import kotlin.time.ExperimentalTime
|
||||
import kotlin.time.toDuration
|
||||
|
||||
@AndroidEntryPoint
|
||||
|
|
@ -238,7 +238,8 @@ class NavigationDrawerFragment : DialogFragment() {
|
|||
|
||||
private fun updateSeasonalMenuEntries(gearEvent: WorldStateEvent?, items: List<Item>) {
|
||||
val market = getItemWithIdentifier(SIDEBAR_SHOPS_MARKET) ?: return
|
||||
if (items.isNotEmpty() && items.firstOrNull()?.event?.end?.after(Date()) == true) {
|
||||
val item = items.firstOrNull()
|
||||
if (item?.isValid() == true && item.event?.end?.after(Date()) == true) {
|
||||
market.pillText = context?.getString(R.string.something_new)
|
||||
market.subtitle = context?.getString(R.string.limited_potions_available)
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -6,6 +6,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.android.habitica.data.InventoryRepository
|
||||
|
|
@ -49,7 +50,6 @@ class StableRecyclerFragment :
|
|||
|
||||
var adapter: StableRecyclerAdapter? = null
|
||||
var itemTypeText: String? = null
|
||||
internal var layoutManager: androidx.recyclerview.widget.GridLayoutManager? = null
|
||||
|
||||
override var binding: FragmentRefreshRecyclerviewBinding? = null
|
||||
|
||||
|
|
@ -70,11 +70,11 @@ class StableRecyclerFragment :
|
|||
)
|
||||
binding?.refreshLayout?.setOnRefreshListener(this)
|
||||
|
||||
layoutManager = androidx.recyclerview.widget.GridLayoutManager(activity, 4)
|
||||
layoutManager?.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
|
||||
val layoutManager = androidx.recyclerview.widget.GridLayoutManager(activity, 4)
|
||||
layoutManager.spanSizeLookup = object : androidx.recyclerview.widget.GridLayoutManager.SpanSizeLookup() {
|
||||
override fun getSpanSize(position: Int): Int {
|
||||
return if (adapter?.getItemViewType(position) == 0 || adapter?.getItemViewType(position) == 1) {
|
||||
layoutManager?.spanCount ?: 1
|
||||
layoutManager.spanCount
|
||||
} else {
|
||||
1
|
||||
}
|
||||
|
|
@ -134,7 +134,7 @@ class StableRecyclerFragment :
|
|||
if (spanCount == 0) {
|
||||
spanCount = 1
|
||||
}
|
||||
layoutManager?.spanCount = spanCount
|
||||
(binding?.recyclerView?.layoutManager as? GridLayoutManager)?.spanCount = spanCount
|
||||
}
|
||||
|
||||
private fun loadItems() {
|
||||
|
|
|
|||
|
|
@ -25,7 +25,6 @@ import com.habitrpg.android.habitica.data.FAQRepository
|
|||
import com.habitrpg.android.habitica.databinding.FragmentFaqOverviewBinding
|
||||
import com.habitrpg.android.habitica.databinding.SupportFaqItemBinding
|
||||
import com.habitrpg.android.habitica.helpers.AppConfigManager
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
|
||||
import com.habitrpg.android.habitica.ui.viewmodels.MainUserViewModel
|
||||
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
|
||||
|
|
@ -34,12 +33,14 @@ import com.habitrpg.common.habitica.extensions.dpToPx
|
|||
import com.habitrpg.common.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.common.habitica.helpers.AppTestingLevel
|
||||
import com.habitrpg.common.habitica.helpers.ExceptionHandler
|
||||
import com.habitrpg.common.habitica.helpers.MainNavigationController
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.common.habitica.models.PlayerTier
|
||||
import com.jaredrummler.android.device.DeviceName
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
import kotlin.math.min
|
||||
|
||||
@AndroidEntryPoint
|
||||
class FAQOverviewFragment : BaseMainFragment<FragmentFaqOverviewBinding>() {
|
||||
|
|
@ -143,7 +144,7 @@ class FAQOverviewFragment : BaseMainFragment<FragmentFaqOverviewBinding>() {
|
|||
ds.isUnderlineText = false
|
||||
}
|
||||
}
|
||||
val startIndex = fullText.indexOf(clickableText)
|
||||
val startIndex = min(0, fullText.indexOf(clickableText))
|
||||
val endIndex = startIndex + clickableText.length
|
||||
spannableString.setSpan(clickableSpan, startIndex, endIndex, Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)
|
||||
|
||||
|
|
|
|||
|
|
@ -25,23 +25,31 @@ import javax.inject.Inject
|
|||
class MainUserViewModel @Inject constructor(private val authenticationHandler: AuthenticationHandler, val userRepository: UserRepository, val socialRepository: SocialRepository) {
|
||||
|
||||
val formattedUsername: CharSequence?
|
||||
get() = user.value?.formattedUsername
|
||||
get() = validatedUser?.formattedUsername
|
||||
val userID: String
|
||||
get() = user.value?.id ?: authenticationHandler.currentUserID ?: ""
|
||||
get() = validatedUser?.id ?: authenticationHandler.currentUserID ?: ""
|
||||
val username: CharSequence
|
||||
get() = user.value?.username ?: ""
|
||||
get() = validatedUser?.username ?: ""
|
||||
val displayName: CharSequence
|
||||
get() = user.value?.profile?.name ?: ""
|
||||
get() = validatedUser?.profile?.name ?: ""
|
||||
val partyID: String?
|
||||
get() = user.value?.party?.id
|
||||
get() = validatedUser?.party?.id
|
||||
val isUserFainted: Boolean
|
||||
get() = (user.value?.stats?.hp ?: 1.0) == 0.0
|
||||
get() = (validatedUser?.stats?.hp ?: 1.0) == 0.0
|
||||
val isUserInParty: Boolean
|
||||
get() = user.value?.hasParty == true
|
||||
get() = validatedUser?.hasParty == true
|
||||
val mirrorGroupTasks: List<String>
|
||||
get() = user.value?.preferences?.tasks?.mirrorGroupTasks ?: emptyList()
|
||||
get() = validatedUser?.preferences?.tasks?.mirrorGroupTasks ?: emptyList()
|
||||
|
||||
val user: LiveData<User?> = userRepository.getUser().asLiveData()
|
||||
private val validatedUser: User?
|
||||
get() {
|
||||
val u = this.user.value
|
||||
if (u?.isValid() == true) {
|
||||
return u
|
||||
}
|
||||
return null
|
||||
}
|
||||
var currentTeamPlan = MutableSharedFlow<TeamPlan?>(
|
||||
replay = 1,
|
||||
onBufferOverflow = BufferOverflow.DROP_OLDEST
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ import android.widget.TextView
|
|||
import androidx.preference.ListPreference
|
||||
import com.habitrpg.android.habitica.R
|
||||
import com.habitrpg.common.habitica.extensions.setScaledPadding
|
||||
import kotlin.math.min
|
||||
|
||||
class HabiticaListPreference : ListPreference {
|
||||
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int, defStyleRes: Int) :
|
||||
|
|
@ -23,8 +24,9 @@ class HabiticaListPreference : ListPreference {
|
|||
val subtitleText = TextView(context)
|
||||
subtitleText.setText(R.string.cds_subtitle)
|
||||
val builder = AlertDialog.Builder(context).setSingleChoiceItems(entries, getValueIndex() + 1) { dialog, index ->
|
||||
if (callChangeListener(entryValues[index - 1].toString())) {
|
||||
setValueIndex(index - 1)
|
||||
val actualIndex = min(0, index - 1)
|
||||
if (callChangeListener(entryValues[actualIndex].toString())) {
|
||||
setValueIndex(actualIndex)
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -9,6 +9,7 @@ import android.view.LayoutInflater
|
|||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import android.view.WindowManager.BadTokenException
|
||||
import android.view.animation.AccelerateInterpolator
|
||||
import android.widget.Button
|
||||
import android.widget.LinearLayout
|
||||
|
|
@ -328,10 +329,14 @@ open class HabiticaAlertDialog(context: Context) : AlertDialog(context, R.style.
|
|||
}
|
||||
|
||||
private fun addToQueue(dialog: HabiticaAlertDialog) {
|
||||
if (checkIfQueueAvailable()) {
|
||||
dialog.show()
|
||||
try {
|
||||
if (checkIfQueueAvailable()) {
|
||||
dialog.show()
|
||||
}
|
||||
dialogQueue.add(dialog)
|
||||
} catch (e: BadTokenException) {
|
||||
// can't show anything
|
||||
}
|
||||
dialogQueue.add(dialog)
|
||||
}
|
||||
|
||||
private fun checkIfQueueAvailable(): Boolean {
|
||||
|
|
|
|||
|
|
@ -26,7 +26,6 @@ import kotlinx.coroutines.CoroutineScope
|
|||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.lang.NullPointerException
|
||||
import java.util.regex.Matcher
|
||||
import java.util.regex.Pattern
|
||||
|
||||
|
|
@ -93,14 +92,17 @@ object MarkdownParser {
|
|||
}
|
||||
|
||||
val preProcessedInput = processMarkdown(input)
|
||||
|
||||
val hashCode = preProcessedInput.hashCode()
|
||||
try {
|
||||
if (cache.containsKey(hashCode)) {
|
||||
return cache[hashCode] ?: SpannableString(preProcessedInput)
|
||||
var hashCode: Int? = null
|
||||
if (preProcessedInput.length < 500) {
|
||||
// caching too large inputs. Since these are unlikely to be in a list, this is fine
|
||||
hashCode = preProcessedInput.hashCode()
|
||||
try {
|
||||
if (cache.containsKey(hashCode)) {
|
||||
return cache[hashCode] ?: SpannableString(preProcessedInput)
|
||||
}
|
||||
} catch (_: NullPointerException) {
|
||||
// Sometimes happens
|
||||
}
|
||||
} catch (_: NullPointerException) {
|
||||
// Sometimes happens
|
||||
}
|
||||
val text = EmojiParser.parseEmojis(preProcessedInput) ?: preProcessedInput
|
||||
// Adding this space here bc for some reason some markdown is not rendered correctly when the whole string is supposed to be formatted
|
||||
|
|
|
|||
|
|
@ -17,7 +17,9 @@ interface Avatar {
|
|||
get() = if (username != null) "@$username" else null
|
||||
|
||||
val gemCount: Int
|
||||
get() = (this.balance * 4).toInt()
|
||||
get() = if (this.isValid()) {
|
||||
(this.balance * 4).toInt()
|
||||
} else { 0 }
|
||||
|
||||
val costume: AvatarOutfit?
|
||||
get() = items?.gear?.costume
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@
|
|||
|
||||
<application
|
||||
android:name="com.habitrpg.wearos.habitica.MainApplication"
|
||||
android:allowBackup="true"
|
||||
android:allowBackup="false"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
android:supportsRtl="true"
|
||||
|
|
|
|||
|
|
@ -14,10 +14,10 @@ import com.google.android.gms.wearable.CapabilityClient
|
|||
import com.google.android.gms.wearable.MessageClient
|
||||
import com.google.android.gms.wearable.Wearable
|
||||
import com.habitrpg.android.habitica.databinding.ActivityWrapperBinding
|
||||
import com.habitrpg.common.habitica.helpers.launchCatching
|
||||
import com.habitrpg.wearos.habitica.managers.AppStateManager
|
||||
import com.habitrpg.wearos.habitica.ui.viewmodels.BaseViewModel
|
||||
import com.habitrpg.wearos.habitica.ui.views.IndeterminateProgressView
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -110,7 +110,7 @@ abstract class BaseActivity<B : ViewBinding, VM : BaseViewModel> : ComponentActi
|
|||
data: ByteArray?,
|
||||
function: ((Boolean) -> Unit)? = null
|
||||
) {
|
||||
lifecycleScope.launch(Dispatchers.IO) {
|
||||
lifecycleScope.launchCatching {
|
||||
val info = Tasks.await(
|
||||
capabilityClient.getCapability(
|
||||
permission,
|
||||
|
|
|
|||
Loading…
Reference in a new issue