Begin moving logic out of main activity

This commit is contained in:
Phillip Thelen 2022-01-28 16:24:13 +01:00
parent a37de3f497
commit 009fc24fbd
38 changed files with 569 additions and 428 deletions

View file

@ -31,7 +31,6 @@ import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.installations.FirebaseInstallations
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import com.google.firebase.remoteconfig.FirebaseRemoteConfigSettings
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.components.AppComponent
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ApiClient
@ -42,14 +41,12 @@ import com.habitrpg.android.habitica.modules.UserModule
import com.habitrpg.android.habitica.modules.UserRepositoryModule
import com.habitrpg.android.habitica.proxy.AnalyticsManager
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.activities.IntroActivity
import com.habitrpg.android.habitica.ui.activities.LoginActivity
import com.habitrpg.android.habitica.ui.helpers.MarkdownParser
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import io.realm.Realm
import io.realm.RealmConfiguration
import java.lang.ref.WeakReference
import java.util.Locale
import javax.inject.Inject
// contains all HabiticaApplicationLogic except dagger componentInitialisation
@ -295,15 +292,6 @@ abstract class HabiticaBaseApplication : Application(), Application.ActivityLife
userComponent = component?.plus(UserModule(), UserRepositoryModule())
}
fun checkUserAuthentication(context: Context, hostConfig: HostConfig?): Boolean {
if (hostConfig?.apiKey == null || hostConfig.apiKey == "" || hostConfig.userID == "") {
startActivity(IntroActivity::class.java, context)
return false
}
return true
}
private fun startActivity(activityClass: Class<*>, context: Context) {
val intent = Intent(context, activityClass)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)

View file

@ -101,6 +101,7 @@ import com.habitrpg.android.habitica.ui.fragments.tasks.TeamBoardFragment;
import com.habitrpg.android.habitica.ui.viewmodels.AuthenticationViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.InboxViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.MainActivityViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel;
import com.habitrpg.android.habitica.ui.viewmodels.inventory.equipment.EquipmentOverviewViewModel;
import com.habitrpg.android.habitica.ui.views.insufficientCurrency.InsufficientGemsDialog;
@ -346,4 +347,6 @@ public interface UserComponent {
void inject(@NotNull AccountPreferenceFragment accountPreferenceFragment);
void inject(@NotNull AuthenticationViewModel authenticationViewModel);
void inject(@NotNull MainActivityViewModel mainActivityViewModel);
}

View file

@ -1,15 +1,12 @@
package com.habitrpg.android.habitica.data
import android.content.Context
import com.habitrpg.android.habitica.models.ContentResult
import com.habitrpg.android.habitica.models.WorldState
import io.reactivex.rxjava3.core.Flowable
interface ContentRepository {
interface ContentRepository: BaseRepository {
fun retrieveContent(forced: Boolean = false): Flowable<ContentResult>
fun retrieveContent(context: Context?): Flowable<ContentResult>
fun retrieveContent(context: Context?, forced: Boolean): Flowable<ContentResult>
fun retrieveWorldState(context: Context?): Flowable<WorldState>
fun retrieveWorldState(): Flowable<WorldState>
fun getWorldState(): Flowable<WorldState>
}

View file

@ -28,7 +28,7 @@ interface UserRepository : BaseRepository {
fun retrieveUser(withTasks: Boolean): Flowable<User>
fun retrieveUser(withTasks: Boolean = false, forced: Boolean = false, overrideExisting: Boolean = false): Flowable<User>
fun revive(user: User): Flowable<User>
fun revive(): Flowable<User>
fun resetTutorial()

View file

@ -12,24 +12,20 @@ import io.reactivex.rxjava3.core.Flowable
import io.realm.RealmList
import java.util.*
abstract class ContentRepositoryImpl<T : ContentLocalRepository>(localRepository: T, apiClient: ApiClient) : BaseRepositoryImpl<T>(localRepository, apiClient), ContentRepository {
class ContentRepositoryImpl<T : ContentLocalRepository>(localRepository: T, apiClient: ApiClient, context: Context) : BaseRepositoryImpl<T>(localRepository, apiClient), ContentRepository {
private val mysteryItem = SpecialItem.makeMysteryItem(context)
private var lastContentSync = 0L
private var lastWorldStateSync = 0L
override fun retrieveContent(context: Context?): Flowable<ContentResult> {
return retrieveContent(context, false)
}
override fun retrieveContent(context: Context?, forced: Boolean): Flowable<ContentResult> {
override fun retrieveContent(forced: Boolean): Flowable<ContentResult> {
val now = Date().time
return if (forced || now - this.lastContentSync > 300000) {
lastContentSync = now
apiClient.content.doOnNext {
context?.let { context ->
it.special = RealmList()
it.special.add(SpecialItem.makeMysteryItem(context))
}
it.special = RealmList()
it.special.add(mysteryItem)
localRepository.saveContent(it)
}
} else {
@ -37,7 +33,7 @@ abstract class ContentRepositoryImpl<T : ContentLocalRepository>(localRepository
}
}
override fun retrieveWorldState(context: Context?): Flowable<WorldState> {
override fun retrieveWorldState(): Flowable<WorldState> {
val now = Date().time
return if (now - this.lastWorldStateSync > 3600000) {
lastWorldStateSync = now

View file

@ -94,9 +94,10 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
}
}
override fun revive(user: User): Flowable<User> =
apiClient.revive().map { newUser -> mergeUser(user, newUser) }
.flatMap { retrieveUser(false, true) }
override fun revive(): Flowable<User> = zipWithLiveUser(apiClient.revive()) { newUser, user ->
mergeUser(user, newUser)
}
.flatMap { retrieveUser(false, true) }
override fun resetTutorial() {
localRepository.getTutorialSteps()

View file

@ -0,0 +1,83 @@
package com.habitrpg.android.habitica.interactors
import android.graphics.Bitmap
import android.graphics.Canvas
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.executors.PostExecutionThread
import com.habitrpg.android.habitica.models.inventory.Food
import com.habitrpg.android.habitica.models.inventory.Pet
import com.habitrpg.android.habitica.models.responses.FeedResponse
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.views.SnackbarActivity
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.reactivex.rxjava3.core.Flowable
import javax.inject.Inject
class FeedPetUseCase @Inject
constructor(
private val inventoryRepository: InventoryRepository,
postExecutionThread: PostExecutionThread
) : UseCase<FeedPetUseCase.RequestValues, FeedResponse>(postExecutionThread) {
override fun buildUseCaseObservable(requestValues: FeedPetUseCase.RequestValues): Flowable<FeedResponse> {
return inventoryRepository.feedPet(requestValues.pet, requestValues.food)
.doOnNext { feedResponse ->
(requestValues.context as? SnackbarActivity)?.showSnackbar(content = feedResponse.message)
if (feedResponse.value == -1) {
val mountWrapper =
View.inflate(
requestValues.context,
R.layout.pet_imageview,
null
) as? FrameLayout
val mountImageView =
mountWrapper?.findViewById(R.id.pet_imageview) as? ImageView
DataBindingUtils.loadImage(
mountImageView,
"Mount_Icon_" + requestValues.pet.key
)
val dialog = HabiticaAlertDialog(requestValues.context)
dialog.setTitle(
requestValues.context.getString(
R.string.evolved_pet_title,
requestValues.pet.text
)
)
dialog.setAdditionalContentView(mountWrapper)
dialog.addButton(R.string.onwards, true)
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val message =
requestValues.context.getString(
R.string.share_raised,
requestValues.pet.text
)
val mountImageSideLength = 99
val sharedImage = Bitmap.createBitmap(
mountImageSideLength,
mountImageSideLength,
Bitmap.Config.ARGB_8888
)
val canvas = Canvas(sharedImage)
mountImageView?.drawable?.setBounds(
0,
0,
mountImageSideLength,
mountImageSideLength
)
mountImageView?.drawable?.draw(canvas)
requestValues.context.shareContent("raisedPet", message, sharedImage)
hatchingDialog.dismiss()
}
dialog.enqueue()
}
}
}
class RequestValues(val pet: Pet, val food: Food, val context: BaseActivity) :
UseCase.RequestValues
}

View file

@ -0,0 +1,54 @@
package com.habitrpg.android.habitica.interactors
import android.graphics.Bitmap
import android.graphics.Canvas
import android.view.View
import android.widget.FrameLayout
import android.widget.ImageView
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.executors.PostExecutionThread
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.models.user.Items
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import io.reactivex.rxjava3.core.Flowable
import javax.inject.Inject
class HatchPetUseCase @Inject
constructor(private val inventoryRepository: InventoryRepository, postExecutionThread: PostExecutionThread) : UseCase<HatchPetUseCase.RequestValues, Items>(postExecutionThread) {
override fun buildUseCaseObservable(requestValues: RequestValues): Flowable<Items> {
return inventoryRepository.hatchPet(requestValues.egg, requestValues.potion) {
val petWrapper = View.inflate(requestValues.context, R.layout.pet_imageview, null) as? FrameLayout
val petImageView = petWrapper?.findViewById(R.id.pet_imageview) as? ImageView
DataBindingUtils.loadImage(petImageView, "stable_Pet-" + requestValues.egg.key + "-" + requestValues.potion.key)
val potionName = requestValues.potion.text
val eggName = requestValues.egg.text
val dialog = HabiticaAlertDialog(requestValues.context)
dialog.setTitle(requestValues.context.getString(R.string.hatched_pet_title, potionName, eggName))
dialog.setAdditionalContentView(petWrapper)
dialog.addButton(R.string.equip, true) { _, _ ->
inventoryRepository.equip(null, "pet", requestValues.egg.key + "-" + requestValues.potion.key)
.subscribe({}, RxErrorHandler.handleEmptyError())
}
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val message = requestValues.context.getString(R.string.share_hatched, potionName, eggName)
val petImageSideLength = 140
val sharedImage = Bitmap.createBitmap(petImageSideLength, petImageSideLength, Bitmap.Config.ARGB_8888)
val canvas = Canvas(sharedImage)
petImageView?.drawable?.setBounds(0, 0, petImageSideLength, petImageSideLength)
petImageView?.drawable?.draw(canvas)
requestValues.context.shareContent("hatchedPet", message, sharedImage)
hatchingDialog.dismiss()
}
dialog.setExtraCloseButtonVisibility(View.VISIBLE)
dialog.enqueue()
}
}
class RequestValues(val potion: HatchingPotion, val egg: Egg, val context: BaseActivity) : UseCase.RequestValues
}

View file

@ -1,25 +0,0 @@
package com.habitrpg.android.habitica.modules;
import com.habitrpg.android.habitica.data.ApiClient;
import com.habitrpg.android.habitica.data.ContentRepository;
import com.habitrpg.android.habitica.data.implementation.ContentRepositoryImpl;
import com.habitrpg.android.habitica.data.local.ContentLocalRepository;
import com.habitrpg.android.habitica.data.local.implementation.RealmContentLocalRepository;
import dagger.Module;
import dagger.Provides;
import io.realm.Realm;
@Module
public class RepositoryModule {
@Provides
public Realm providesRealm() {
return Realm.getDefaultInstance();
}
@Provides
public ContentLocalRepository providesContentLocalRepository(Realm realm) { return new RealmContentLocalRepository(realm); }
@Provides
public ContentRepository providesContentRepository(ContentLocalRepository contentLocalRepository, ApiClient apiClient) { return new ContentRepositoryImpl<ContentLocalRepository>(contentLocalRepository, apiClient) {}; }
}

View file

@ -0,0 +1,37 @@
package com.habitrpg.android.habitica.modules
import android.content.Context
import com.habitrpg.android.habitica.data.local.ContentLocalRepository
import com.habitrpg.android.habitica.data.local.implementation.RealmContentLocalRepository
import com.habitrpg.android.habitica.data.ApiClient
import com.habitrpg.android.habitica.data.ContentRepository
import com.habitrpg.android.habitica.data.implementation.ContentRepositoryImpl
import dagger.Module
import dagger.Provides
import io.realm.Realm
@Module
open class RepositoryModule {
@Provides
open fun providesRealm(): Realm? {
return Realm.getDefaultInstance()
}
@Provides
fun providesContentLocalRepository(realm: Realm?): ContentLocalRepository {
return RealmContentLocalRepository(realm!!)
}
@Provides
fun providesContentRepository(
contentLocalRepository: ContentLocalRepository,
apiClient: ApiClient,
context: Context
): ContentRepository {
return ContentRepositoryImpl(
contentLocalRepository,
apiClient,
context
)
}
}

View file

@ -65,15 +65,15 @@ class UserRepositoryModule {
@Provides
fun providesTaskRepository(
localRepository: TaskLocalRepository?,
apiClient: ApiClient?,
@Named(AppModule.NAMED_USER_ID) userId: String?,
appConfigManager: AppConfigManager?,
analyticsManager: AnalyticsManager?
localRepository: TaskLocalRepository,
apiClient: ApiClient,
@Named(AppModule.NAMED_USER_ID) userId: String,
appConfigManager: AppConfigManager,
analyticsManager: AnalyticsManager
): TaskRepository {
return TaskRepositoryImpl(
localRepository!!,
apiClient!!,
localRepository,
apiClient,
userId!!,
appConfigManager!!,
analyticsManager!!

View file

@ -5,6 +5,7 @@ import android.os.Bundle
interface AnalyticsManager {
fun logException(t: Throwable)
fun setUserIdentifier(identifier: String)
fun setUserProperty(identifier: String, value: String)
fun logError(msg: String)
fun logEvent(eventName: String, data: Bundle)
}

View file

@ -5,7 +5,7 @@ import com.habitrpg.android.habitica.proxy.AnalyticsManager
class EmptyAnalyticsManager : AnalyticsManager {
override fun logException(e: Throwable) {
override fun logException(t: Throwable) {
// pass
}
@ -13,6 +13,10 @@ class EmptyAnalyticsManager : AnalyticsManager {
// pass
}
override fun setUserProperty(identifier: String, value: String) {
// pass
}
override fun logError(msg: String) {
// pass
}

View file

@ -128,7 +128,6 @@ abstract class BaseActivity : AppCompatActivity() {
internal open fun loadTheme(sharedPreferences: SharedPreferences, forced: Boolean = false) {
val theme = forcedTheme ?: sharedPreferences.getString("theme_name", "purple")
val modernHeaderStyle = overrideModernHeader ?: sharedPreferences.getBoolean("modern_header_style", true)
if (theme != currentTheme || forced) {
if (forcedIsNight ?: isNightMode) {
setTheme(
@ -165,10 +164,8 @@ abstract class BaseActivity : AppCompatActivity() {
} else {
getThemeColor(R.attr.colorPrimaryDark)
}
if (!(forcedIsNight ?: isNightMode) && modernHeaderStyle) {
if (!(forcedIsNight ?: isNightMode)) {
window.updateStatusBarColor(getThemeColor(R.attr.headerBackgroundColor), true)
} else {
window.updateStatusBarColor(getThemeColor(R.attr.statusBarBackground), false)
}
if (currentTheme != null && theme != currentTheme) {

View file

@ -44,7 +44,7 @@ class IntroActivity : BaseActivity(), View.OnClickListener, ViewPager.OnPageChan
binding.skipButton.setOnClickListener(this)
binding.finishButton.setOnClickListener(this)
compositeSubscription.add(contentRepository.retrieveContent(this).subscribe({ }, RxErrorHandler.handleEmptyError()))
compositeSubscription.add(contentRepository.retrieveContent().subscribe({ }, RxErrorHandler.handleEmptyError()))
window.statusBarColor = ContextCompat.getColor(this, R.color.black_20_alpha)
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)

View file

@ -5,37 +5,30 @@ import android.app.NotificationManager
import android.appwidget.AppWidgetManager
import android.content.ComponentName
import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.content.res.Configuration
import android.graphics.Bitmap
import android.graphics.Canvas
import android.os.Build
import android.os.Bundle
import android.util.Log
import android.view.*
import android.widget.FrameLayout
import android.widget.ImageView
import androidx.activity.viewModels
import androidx.appcompat.app.ActionBarDrawerToggle
import androidx.core.content.edit
import androidx.drawerlayout.widget.DrawerLayout
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavDestination
import androidx.navigation.findNavController
import androidx.navigation.fragment.NavHostFragment
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.perf.FirebasePerformance
import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.api.MaintenanceApiService
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.*
import com.habitrpg.android.habitica.data.local.UserQuestStatus
import com.habitrpg.android.habitica.databinding.ActivityMainBinding
import com.habitrpg.android.habitica.databinding.DialogFaintBinding
import com.habitrpg.android.habitica.extensions.*
import com.habitrpg.android.habitica.helpers.*
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager
import com.habitrpg.android.habitica.interactors.CheckClassSelectionUseCase
import com.habitrpg.android.habitica.interactors.DisplayItemDropUseCase
import com.habitrpg.android.habitica.interactors.NotifyUserUseCase
@ -52,6 +45,7 @@ import com.habitrpg.android.habitica.ui.AvatarWithBarsViewModel
import com.habitrpg.android.habitica.ui.TutorialView
import com.habitrpg.android.habitica.ui.fragments.NavigationDrawerFragment
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.viewmodels.MainActivityViewModel
import com.habitrpg.android.habitica.ui.viewmodels.NotificationsViewModel
import com.habitrpg.android.habitica.ui.views.*
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar.SnackbarDisplayType
@ -63,9 +57,6 @@ import com.habitrpg.android.habitica.widget.HabitButtonWidgetProvider
import com.habitrpg.android.habitica.widget.TodoListWidgetProvider
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Observable
import io.reactivex.rxjava3.functions.Consumer
import io.reactivex.rxjava3.schedulers.Schedulers
import io.realm.kotlin.isValid
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
@ -78,14 +69,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
@Inject
internal lateinit var soundManager: SoundManager
@Inject
internal lateinit var maintenanceService: MaintenanceApiService
@Inject
internal lateinit var hostConfig: HostConfig
@Inject
internal lateinit var sharedPreferences: SharedPreferences
@Inject
internal lateinit var pushNotificationManager: PushNotificationManager
@Inject
internal lateinit var checkClassSelectionUseCase: CheckClassSelectionUseCase
@Inject
internal lateinit var displayItemDropUseCase: DisplayItemDropUseCase
@ -98,20 +81,16 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
@Inject
internal lateinit var inventoryRepository: InventoryRepository
@Inject
internal lateinit var contentRepository: ContentRepository
@Inject
internal lateinit var taskAlarmManager: TaskAlarmManager
@Inject
internal lateinit var appConfigManager: AppConfigManager
lateinit var binding: ActivityMainBinding
val snackbarContainer: ViewGroup
get() = binding.snackbarContainer
var user: User? = null
private var avatarInHeader: AvatarWithBarsViewModel? = null
var notificationsViewModel: NotificationsViewModel? = null
val notificationsViewModel: NotificationsViewModel by viewModels()
val viewModel: MainActivityViewModel by viewModels()
private var faintDialog: HabiticaAlertDialog? = null
private var sideAvatarView: AvatarView? = null
private var activeTutorialView: TutorialView? = null
@ -121,9 +100,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
private var userQuestStatus = UserQuestStatus.NO_QUEST
private var lastNotificationOpen: Long? = null
val userID: String
get() = user?.id ?: ""
val isAppBarExpanded: Boolean
get() = binding.appbar.height - binding.appbar.bottom == 0
@ -147,7 +123,10 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
launchTrace?.start()
super.onCreate(savedInstanceState)
if (!HabiticaBaseApplication.checkUserAuthentication(this, hostConfig)) {
if (!viewModel.isAuthenticated) {
val intent = Intent(this, IntroActivity::class.java)
intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_CLEAR_TASK or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(intent)
return
}
@ -156,36 +135,24 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
avatarInHeader = AvatarWithBarsViewModel(this, binding.avatarWithBars, userRepository)
sideAvatarView = AvatarView(this, showBackground = true, showMount = false, showPet = false)
compositeSubscription.add(
userRepository.getUser()
.subscribe(
{ newUser ->
this@MainActivity.user = newUser
this@MainActivity.setUserData()
},
RxErrorHandler.handleEmptyError()
)
)
viewModel.user.observe(this) {
setUserData(it)
}
compositeSubscription.add(
userRepository.getUserQuestStatus().subscribeWithErrorHandler {
userQuestStatus = it
}
)
val viewModel = ViewModelProvider(this).get(NotificationsViewModel::class.java)
notificationsViewModel = viewModel
val drawerLayout = findViewById<DrawerLayout>(R.id.drawer_layout)
drawerFragment = supportFragmentManager.findFragmentById(R.id.navigation_drawer) as? NavigationDrawerFragment
drawerFragment?.setUp(R.id.navigation_drawer, drawerLayout, viewModel)
drawerFragment?.setUp(R.id.navigation_drawer, drawerLayout, notificationsViewModel)
drawerToggle = object : ActionBarDrawerToggle(
this, /* host Activity */
drawerLayout, /* DrawerLayout object */
R.string.navigation_drawer_open, /* "open drawer" description */
R.string.navigation_drawer_close /* "close drawer" description */
this,
drawerLayout,
R.string.navigation_drawer_open,
R.string.navigation_drawer_close
) {}
// Set the drawer toggle as the DrawerListener
drawerToggle?.let { drawerLayout.addDrawerListener(it) }
@ -193,8 +160,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
private var isOpeningDrawer: Boolean? = null
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
val modernHeaderStyle = sharedPreferences.getBoolean("modern_header_style", true)
if (!isUsingNightModeResources() && modernHeaderStyle) {
if (!isUsingNightModeResources()) {
if (slideOffset < 0.5f && isOpeningDrawer == null) {
window.updateStatusBarColor(getThemeColor(R.attr.colorPrimaryDark), false)
isOpeningDrawer = true
@ -207,18 +173,15 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
override fun onDrawerOpened(drawerView: View) {
hideKeyboard()
val modernHeaderStyle = sharedPreferences.getBoolean("modern_header_style", true)
if (!isUsingNightModeResources() && modernHeaderStyle) {
if (!isUsingNightModeResources()) {
window.updateStatusBarColor(getThemeColor(R.attr.colorPrimaryDark), false)
}
isOpeningDrawer = null
drawerFragment?.updatePromo()
}
override fun onDrawerClosed(drawerView: View) {
val modernHeaderStyle = sharedPreferences.getBoolean("modern_header_style", true)
if (!isUsingNightModeResources() && modernHeaderStyle) {
if (!isUsingNightModeResources()) {
window.updateStatusBarColor(getThemeColor(R.attr.headerBackgroundColor), true)
}
isOpeningDrawer = null
@ -233,11 +196,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
setupNotifications()
setupBottomnavigationLayoutListener()
try {
taskAlarmManager.scheduleAllSavedAlarms(sharedPreferences.getBoolean("preventDailyReminder", false))
} catch (e: Exception) {
analyticsManager.logException(e)
}
viewModel.onCreate()
}
override fun setTitle(title: CharSequence?) {
@ -249,32 +208,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
}
private fun updateToolbarTitle(destination: NavDestination, arguments: Bundle?) {
title = if (destination.id == R.id.promoInfoFragment) {
""
} else if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) {
arguments?.getString("type")
} else if (destination.label.isNullOrEmpty() && user?.isValid == true) {
user?.profile?.name
} else if (destination.label != null) {
destination.label
} else {
""
}
if (destination.id == R.id.petDetailRecyclerFragment || destination.id == R.id.mountDetailRecyclerFragment) {
compositeSubscription.add(
inventoryRepository.getItem("egg", arguments?.getString("type") ?: "").firstElement().subscribe(
{
if (!it.isValid()) return@subscribe
binding.toolbarTitle.text = if (destination.id == R.id.petDetailRecyclerFragment) {
(it as? Egg)?.text
} else {
(it as? Egg)?.mountText
}
},
RxErrorHandler.handleEmptyError()
)
)
}
viewModel.getToolbarTitle(destination.id, destination.label, arguments?.getString("type")) { title = it }
drawerFragment?.setSelection(destination.id, null, false)
}
@ -312,12 +246,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
// Sync the toggle state after onRestoreInstanceState has occurred.
drawerToggle?.syncState()
launchScreen = sharedPreferences.getString("launch_screen", "")
}
override fun onRestoreInstanceState(savedInstanceState: Bundle) {
super.onRestoreInstanceState(savedInstanceState)
Log.e("RESTORED:", savedInstanceState.toString())
launchScreen = viewModel.launchScreen
}
override fun onConfigurationChanged(newConfig: Configuration) {
@ -338,13 +267,15 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
override fun onResume() {
super.onResume()
viewModel.onResume()
val navHostFragment = supportFragmentManager.findFragmentById(R.id.nav_host_fragment) as NavHostFragment
val navigationController = navHostFragment.navController
MainNavigationController.setup(navigationController)
navigationController.addOnDestinationChangedListener { _, destination, arguments -> updateToolbarTitle(destination, arguments) }
if (launchScreen == "/party") {
if (user == null || user?.party?.id != null) {
if (viewModel.isUserInParty) {
MainNavigationController.navigate(R.id.partyFragment)
}
}
@ -356,12 +287,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
}
resumeFromActivity = false
// Track when the app was last opened, so that we can use this to send out special reminders after a week of inactivity
sharedPreferences.edit {
putLong("lastAppLaunch", Date().time)
putBoolean("preventDailyReminder", false)
}
if ((intent.hasExtra("notificationIdentifier") || intent.hasExtra("openURL")) && lastNotificationOpen != intent.getLongExtra("notificationTimeStamp", 0)) {
lastNotificationOpen = intent.getLongExtra("notificationTimeStamp", 0)
val identifier = intent.getStringExtra("notificationIdentifier") ?: ""
@ -421,51 +346,40 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
findNavController(R.id.nav_host_fragment).navigate(transitionId)
}
private fun setUserData() {
private fun setUserData(user: User?) {
if (user != null) {
val preferences = user?.preferences
val preferences = user.preferences
preferences?.language?.let { apiClient.setLanguageCode(it) }
if (preferences?.language != sharedPreferences.getString("language", "en")) {
sharedPreferences.edit {
putString("language", preferences?.language)
}
if (preferences?.language != viewModel.preferenceLanguage) {
viewModel.preferenceLanguage = preferences?.language
}
preferences?.sound?.let { soundManager.soundTheme = it }
displayDeathDialogIfNeeded()
YesterdailyDialog.showDialogIfNeeded(this, user?.id, userRepository, taskRepository)
YesterdailyDialog.showDialogIfNeeded(this, user.id, userRepository, taskRepository)
if (user?.flags?.verifiedUsername == false && isActivityVisible) {
if (user.flags?.verifiedUsername == false && isActivityVisible) {
val intent = Intent(this, VerifyUsernameActivity::class.java)
startActivity(intent)
}
val quest = user?.party?.quest
val quest = user.party?.quest
if (quest?.completed?.isNotBlank() == true) {
compositeSubscription.add(
inventoryRepository.getQuestContent(user?.party?.quest?.completed ?: "").firstElement().subscribe(
inventoryRepository.getQuestContent(user.party?.quest?.completed ?: "").firstElement().subscribe(
{
QuestCompletedDialog.showWithQuest(this, it)
userRepository.updateUser("party.quest.completed", "").subscribe({}, RxErrorHandler.handleEmptyError())
viewModel.updateUser("party.quest.completed", "")
},
RxErrorHandler.handleEmptyError()
)
)
}
if (user?.flags?.welcomed == false) {
compositeSubscription.add(userRepository.updateUser("flags.welcomed", true).subscribe({}, RxErrorHandler.handleEmptyError()))
}
try {
val navigationController = findNavController(R.id.nav_host_fragment)
if (binding.toolbarTitle.text?.isNotBlank() != true) {
navigationController.currentDestination?.let { updateToolbarTitle(it, null) }
}
} catch (e: java.lang.IllegalStateException) {
// Has no Navcontroller right now.
if (user.flags?.welcomed == false) {
viewModel.updateUser("flags.welcomed", true)
}
}
}
@ -491,44 +405,10 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
inventoryRepository.close()
super.onDestroy()
}
fun feedPet(pet: Pet, food: Food) {
compositeSubscription.add(
this.inventoryRepository.feedPet(pet, food)
.subscribe(
{ feedResponse ->
HabiticaSnackbar.showSnackbar(snackbarContainer, feedResponse.message, SnackbarDisplayType.NORMAL)
if (feedResponse.value == -1) {
val mountWrapper = View.inflate(this, R.layout.pet_imageview, null) as? FrameLayout
val mountImageView = mountWrapper?.findViewById(R.id.pet_imageview) as? ImageView
DataBindingUtils.loadImage(mountImageView, "Mount_Icon_" + pet.key)
val dialog = HabiticaAlertDialog(this@MainActivity)
dialog.setTitle(getString(R.string.evolved_pet_title, pet.text))
dialog.setAdditionalContentView(mountWrapper)
dialog.addButton(R.string.onwards, true)
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val message = getString(R.string.share_raised, pet.text)
val mountImageSideLength = 99
val sharedImage = Bitmap.createBitmap(mountImageSideLength, mountImageSideLength, Bitmap.Config.ARGB_8888)
val canvas = Canvas(sharedImage)
mountImageView?.drawable?.setBounds(0, 0, mountImageSideLength, mountImageSideLength)
mountImageView?.drawable?.draw(canvas)
shareContent("raisedPet", message, sharedImage)
hatchingDialog.dismiss()
}
dialog.enqueue()
}
},
RxErrorHandler.handleEmptyError()
)
)
}
// endregion
internal fun displayTaskScoringResponse(data: TaskScoringResult?) {
if (user != null && data != null) {
if (viewModel.user.value != null && data != null) {
val damageValue = when (userQuestStatus) {
UserQuestStatus.QUEST_BOSS -> data.questDamage
else -> 0.0
@ -537,7 +417,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
notifyUserUseCase.observable(
NotifyUserUseCase.RequestValues(
this, snackbarContainer,
user, data.experienceDelta, data.healthDelta, data.goldDelta, data.manaDelta, damageValue, data.hasLeveledUp, data.level
viewModel.user.value, data.experienceDelta, data.healthDelta, data.goldDelta, data.manaDelta, damageValue, data.hasLeveledUp, data.level
)
)
.subscribe({ }, RxErrorHandler.handleEmptyError())
@ -552,31 +432,22 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
}
private fun displayDeathDialogIfNeeded() {
if (user?.stats?.hp ?: 1.0 > 0) {
if (!viewModel.isUserFainted) {
return
}
if (this.faintDialog == null && !this.isFinishing) {
val customView = View.inflate(this, R.layout.dialog_faint, null)
if (customView != null) {
val hpBarView = customView.findViewById<View>(R.id.hpBar) as? ValueBar
hpBarView?.setLightBackground(true)
hpBarView?.setIcon(HabiticaIconsHelper.imageOfHeartLightBg())
val dialogAvatarView = customView.findViewById<View>(R.id.avatarView) as? AvatarView
user?.let { dialogAvatarView?.setAvatar(it) }
}
val binding = DialogFaintBinding.inflate(this.layoutInflater)
binding.hpBar.setLightBackground(true)
binding.hpBar.setIcon(HabiticaIconsHelper.imageOfHeartLightBg())
viewModel.user.value?.let { binding.avatarView.setAvatar(it) }
this.faintDialog = HabiticaAlertDialog(this)
faintDialog?.setTitle(R.string.faint_header)
faintDialog?.setAdditionalContentView(customView)
faintDialog?.setAdditionalContentView(binding.root)
faintDialog?.addButton(R.string.faint_button, true) { _, _ ->
faintDialog = null
user?.let {
userRepository.revive(it).subscribe({ }, RxErrorHandler.handleEmptyError())
}
userRepository.revive().subscribe({ }, RxErrorHandler.handleEmptyError())
}
soundManager.loadAndPlayAudio(SoundManager.SoundDeath)
this.faintDialog?.enqueue()
@ -593,23 +464,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
}
protected fun retrieveUser(forced: Boolean = false) {
if (hostConfig.hasAuthentication()) {
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")
FirebaseAnalytics.getInstance(this).setUserProperty("checkin_count", user1.loginIncentives.toString())
FirebaseAnalytics.getInstance(this).setUserProperty("level", user1.stats?.lvl?.toString() ?: "")
pushNotificationManager.setUser(user1)
pushNotificationManager.addPushDeviceUsingStoredToken()
}
.flatMap { userRepository.retrieveTeamPlans() }
.flatMap { contentRepository.retrieveContent(this) }
.subscribe({ }, RxErrorHandler.handleEmptyError())
)
}
viewModel.retrieveUser(forced)
}
fun displayTutorialStep(step: TutorialStep, text: String, canBeDeferred: Boolean) {
@ -620,12 +475,7 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
view.onReaction = this
view.setCanBeDeferred(canBeDeferred)
binding.overlayFrameLayout.addView(view)
val additionalData = HashMap<String, Any>()
additionalData["eventLabel"] = step.identifier + "-android"
additionalData["eventValue"] = step.identifier ?: ""
additionalData["complete"] = false
AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
viewModel.logTutorialStatus(step, false)
}
fun displayTutorialStep(step: TutorialStep, texts: List<String>, canBeDeferred: Boolean) {
@ -636,27 +486,15 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
view.onReaction = this
view.setCanBeDeferred(canBeDeferred)
binding.overlayFrameLayout.addView(view)
val additionalData = HashMap<String, Any>()
additionalData["eventLabel"] = step.identifier + "-android"
additionalData["eventValue"] = step.identifier ?: ""
additionalData["complete"] = false
AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
viewModel.logTutorialStatus(step, false)
}
override fun onTutorialCompleted(step: TutorialStep) {
compositeSubscription.add(
userRepository.updateUser("flags.tutorial." + step.tutorialGroup + "." + step.identifier, true)
.subscribe({ }, RxErrorHandler.handleEmptyError())
)
viewModel.updateUser("flags.tutorial." + step.tutorialGroup + "." + step.identifier, true)
binding.overlayFrameLayout.removeView(this.activeTutorialView)
this.removeActiveTutorialView()
val additionalData = HashMap<String, Any>()
additionalData["eventLabel"] = step.identifier + "-android"
additionalData["eventValue"] = step.identifier ?: ""
additionalData["complete"] = true
AmplitudeManager.sendEvent("tutorial", AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR, AmplitudeManager.EVENT_HITTYPE_EVENT, additionalData)
viewModel.logTutorialStatus(step, true)
}
override fun onTutorialDeferred(step: TutorialStep) {
@ -672,36 +510,25 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
}
private fun checkMaintenance() {
compositeSubscription.add(
this.maintenanceService.maintenanceStatus
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
Consumer { maintenanceResponse ->
if (maintenanceResponse == null) {
return@Consumer
}
if (maintenanceResponse.activeMaintenance) {
val intent = createMaintenanceIntent(maintenanceResponse, false)
viewModel.ifNeedsMaintenance { maintenanceResponse ->
if (maintenanceResponse.activeMaintenance) {
val intent = createMaintenanceIntent(maintenanceResponse, false)
startActivity(intent)
} else {
if (maintenanceResponse.minBuild != null) {
try {
val packageInfo = packageManager.getPackageInfo(packageName, 0)
@Suppress("DEPRECATION")
if (packageInfo.versionCode < maintenanceResponse.minBuild) {
val intent = createMaintenanceIntent(maintenanceResponse, true)
startActivity(intent)
} else {
if (maintenanceResponse.minBuild != null) {
try {
val packageInfo = packageManager.getPackageInfo(packageName, 0)
@Suppress("DEPRECATION")
if (packageInfo.versionCode < maintenanceResponse.minBuild) {
val intent = createMaintenanceIntent(maintenanceResponse, true)
startActivity(intent)
}
} catch (e: PackageManager.NameNotFoundException) {
RxErrorHandler.reportError(e)
}
}
}
},
RxErrorHandler.handleEmptyError()
)
)
} catch (e: PackageManager.NameNotFoundException) {
RxErrorHandler.reportError(e)
}
}
}
}
}
private fun createMaintenanceIntent(maintenanceResponse: MaintenanceResponse, isDeprecationNotice: Boolean): Intent {
@ -737,36 +564,4 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
)
}
}
fun hatchPet(potion: HatchingPotion, egg: Egg) {
compositeSubscription.add(
this.inventoryRepository.hatchPet(egg, potion) {
val petWrapper = View.inflate(this, R.layout.pet_imageview, null) as? FrameLayout
val petImageView = petWrapper?.findViewById(R.id.pet_imageview) as? ImageView
DataBindingUtils.loadImage(petImageView, "stable_Pet-" + egg.key + "-" + potion.key)
val potionName = potion.text
val eggName = egg.text
val dialog = HabiticaAlertDialog(this)
dialog.setTitle(getString(R.string.hatched_pet_title, potionName, eggName))
dialog.setAdditionalContentView(petWrapper)
dialog.addButton(R.string.equip, true) { _, _ ->
inventoryRepository.equip(user, "pet", egg.key + "-" + potion.key)
.subscribe({}, RxErrorHandler.handleEmptyError())
}
dialog.addButton(R.string.share, false) { hatchingDialog, _ ->
val message = getString(R.string.share_hatched, potionName, eggName)
val petImageSideLength = 140
val sharedImage = Bitmap.createBitmap(petImageSideLength, petImageSideLength, Bitmap.Config.ARGB_8888)
val canvas = Canvas(sharedImage)
petImageView?.drawable?.setBounds(0, 0, petImageSideLength, petImageSideLength)
petImageView?.drawable?.draw(canvas)
shareContent("hatchedPet", message, sharedImage)
hatchingDialog.dismiss()
}
dialog.setExtraCloseButtonVisibility(View.VISIBLE)
dialog.enqueue()
}.subscribe({ }, RxErrorHandler.handleEmptyError())
)
}
}

View file

@ -7,8 +7,8 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.widget.*
import androidx.activity.viewModels
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
@ -30,7 +30,7 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
@Inject
lateinit var socialRepository: SocialRepository
lateinit var viewModel: NotificationsViewModel
val viewModel: NotificationsViewModel by viewModels()
var inflater: LayoutInflater? = null
@ -50,8 +50,6 @@ class NotificationsActivity : BaseActivity(), androidx.swiperefreshlayout.widget
inflater = getSystemService(Context.LAYOUT_INFLATER_SERVICE) as? LayoutInflater
viewModel = ViewModelProvider(this).get(NotificationsViewModel::class.java)
compositeSubscription.add(
viewModel.getNotifications().subscribe(
{

View file

@ -10,6 +10,7 @@ import com.habitrpg.android.habitica.HabiticaBaseApplication
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.TutorialRepository
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.proxy.AnalyticsManager
import com.habitrpg.android.habitica.ui.activities.MainActivity
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.disposables.CompositeDisposable
@ -25,6 +26,8 @@ abstract class BaseFragment<VB : ViewBinding> : Fragment() {
@Inject
lateinit var tutorialRepository: TutorialRepository
@Inject
lateinit var analyticsManager: AnalyticsManager
var tutorialStepIdentifier: String? = null
var tutorialText: String? = null

View file

@ -44,8 +44,8 @@ abstract class BaseMainFragment<VB : ViewBinding> : BaseFragment<VB>() {
override fun onAttach(context: Context) {
super.onAttach(context)
if (getActivity()?.javaClass == MainActivity::class.java) {
user = activity?.user
if (activity is MainActivity) {
user = activity?.viewModel?.user?.value
}
}

View file

@ -16,9 +16,12 @@ import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.interactors.FeedPetUseCase
import com.habitrpg.android.habitica.interactors.HatchPetUseCase
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseDialogFragment
@ -36,6 +39,11 @@ class ItemDialogFragment : BaseDialogFragment<FragmentItemsBinding>(), SwipeRefr
lateinit var socialRepository: SocialRepository
@Inject
lateinit var userRepository: UserRepository
@Inject
lateinit var hatchPetUseCase: HatchPetUseCase
@Inject
lateinit var feedPetUseCase: FeedPetUseCase
var adapter: ItemRecyclerAdapter? = null
var itemType: String? = null
var itemTypeText: String? = null
@ -194,7 +202,14 @@ class ItemDialogFragment : BaseDialogFragment<FragmentItemsBinding>(), SwipeRefr
}
private fun feedPet(food: Food) {
feedingPet?.let { (activity as? MainActivity)?.feedPet(it, food) }
val pet = feedingPet ?: return
(activity as? BaseActivity)?.let {
compositeSubscription.add(feedPetUseCase.observable(
FeedPetUseCase.RequestValues(
pet, food,
it
)).subscribeWithErrorHandler {})
}
}
override fun onResume() {
@ -225,7 +240,13 @@ class ItemDialogFragment : BaseDialogFragment<FragmentItemsBinding>(), SwipeRefr
private fun hatchPet(potion: HatchingPotion, egg: Egg) {
dismiss()
(activity as? MainActivity)?.hatchPet(potion, egg)
(activity as? BaseActivity)?.let {
compositeSubscription.add(hatchPetUseCase.observable(
HatchPetUseCase.RequestValues(
potion, egg,
it
)).subscribeWithErrorHandler {})
}
}
private fun loadItems() {

View file

@ -15,9 +15,12 @@ import com.habitrpg.android.habitica.extensions.addCloseButton
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.MainNavigationController
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.interactors.HatchPetUseCase
import com.habitrpg.android.habitica.interactors.NotifyUserUseCase
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.models.user.User
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.adapter.inventory.ItemRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseFragment
@ -35,6 +38,9 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
lateinit var socialRepository: SocialRepository
@Inject
lateinit var userRepository: UserRepository
@Inject
internal lateinit var hatchPetUseCase: HatchPetUseCase
var adapter: ItemRecyclerAdapter? = null
var itemType: String? = null
var itemTypeText: String? = null
@ -178,7 +184,13 @@ class ItemRecyclerFragment : BaseFragment<FragmentItemsBinding>(), SwipeRefreshL
}
private fun hatchPet(potion: HatchingPotion, egg: Egg) {
(activity as? MainActivity)?.hatchPet(potion, egg)
(activity as? BaseActivity)?.let {
compositeSubscription.add(hatchPetUseCase.observable(
HatchPetUseCase.RequestValues(
potion, egg,
it
)).subscribeWithErrorHandler {})
}
}
private fun loadItems() {

View file

@ -149,7 +149,7 @@ open class ShopFragment : BaseMainFragment<FragmentRefreshRecyclerviewBinding>()
currencyView.hourglassVisibility = View.GONE
context?.let { FirebaseAnalytics.getInstance(it).logEvent("open_shop", bundleOf(Pair("shopIdentifier", shopIdentifier))) }
context?.let { analyticsManager.logEvent("open_shop", bundleOf(Pair("shopIdentifier", shopIdentifier))) }
}
private fun showClassChangeDialog(classIdentifier: String) {

View file

@ -10,10 +10,14 @@ import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.databinding.FragmentRefreshRecyclerviewBinding
import com.habitrpg.android.habitica.extensions.getTranslatedType
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.interactors.FeedPetUseCase
import com.habitrpg.android.habitica.interactors.HatchPetUseCase
import com.habitrpg.android.habitica.models.inventory.*
import com.habitrpg.android.habitica.models.user.OwnedMount
import com.habitrpg.android.habitica.models.user.OwnedPet
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.adapter.inventory.PetDetailRecyclerAdapter
import com.habitrpg.android.habitica.ui.fragments.BaseMainFragment
import com.habitrpg.android.habitica.ui.fragments.inventory.items.ItemDialogFragment
@ -28,6 +32,8 @@ class PetDetailRecyclerFragment :
@Inject
lateinit var inventoryRepository: InventoryRepository
@Inject
lateinit var feedPetUseCase: FeedPetUseCase
var adapter: PetDetailRecyclerAdapter = PetDetailRecyclerAdapter()
private var animalType: String? = null
@ -192,7 +198,13 @@ class PetDetailRecyclerFragment :
private fun showFeedingDialog(pet: Pet, food: Food?) {
if (food != null) {
activity?.feedPet(pet, food)
(activity as? BaseActivity)?.let {
compositeSubscription.add(feedPetUseCase.observable(
FeedPetUseCase.RequestValues(
pet, food,
it
)).subscribeWithErrorHandler {})
}
return
}
val fragment = ItemDialogFragment()

View file

@ -139,7 +139,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
(activity as? SnackbarActivity)?.showSnackbar(
content = context?.getString(R.string.reloading_content)
)
contentRepository.retrieveContent(context, true).subscribe(
contentRepository.retrieveContent(true).subscribe(
{
(activity as? SnackbarActivity)?.showSnackbar(
content = context?.getString(R.string.reloaded_content),
@ -220,7 +220,7 @@ class PreferencesFragment : BasePreferencesFragment(), SharedPreferences.OnShare
}
userRepository.updateLanguage(languageHelper.languageCode ?: "en")
.flatMap { contentRepository.retrieveContent(context, true) }
.flatMap { contentRepository.retrieveContent(true) }
.subscribe({ }, RxErrorHandler.handleEmptyError())
val intent = Intent(activity, MainActivity::class.java)

View file

@ -23,8 +23,6 @@ import javax.inject.Inject
class GiftPurchaseGemsFragment : BaseFragment<FragmentGiftGemPurchaseBinding>() {
@Inject
lateinit var analyticsManager: AnalyticsManager
@Inject
lateinit var socialRepository: SocialRepository

View file

@ -45,8 +45,6 @@ class SubscriptionFragment : BaseFragment<FragmentSubscriptionBinding>() {
return FragmentSubscriptionBinding.inflate(inflater, container, false)
}
@Inject
lateinit var analyticsManager: AnalyticsManager
@Inject
lateinit var userRepository: UserRepository
@Inject

View file

@ -31,7 +31,11 @@ import io.reactivex.rxjava3.disposables.Disposable
import java.util.concurrent.TimeUnit
import javax.inject.Inject
class ChatFragment : BaseFragment<FragmentChatBinding>() {
class ChatFragment() : BaseFragment<FragmentChatBinding>() {
constructor(viewModel: GroupViewModel) : this() {
this.viewModel = viewModel
}
override var binding: FragmentChatBinding? = null
@ -67,9 +71,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
chatAdapter?.let { adapter ->
compositeSubscription.add(
adapter.getUserLabelClickFlowable().subscribe(
{ userId ->
FullProfileActivity.open(userId)
},
{ userId -> FullProfileActivity.open(userId) },
RxErrorHandler.handleEmptyError()
)
)
@ -95,12 +97,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
}
})
viewModel?.chatmessages?.observe(
viewLifecycleOwner,
{
setChatMessages(it)
}
)
viewModel?.chatmessages?.observe(viewLifecycleOwner, { setChatMessages(it) })
binding?.chatBarView?.onCommunityGuidelinesAccepted = {
viewModel?.updateUser("flags.communityGuidelinesAccepted", true)
@ -131,7 +128,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
}
private fun startAutoRefreshing() {
if (refreshDisposable != null && refreshDisposable?.isDisposed != true) {
if (refreshDisposable?.isDisposed != true) {
refreshDisposable?.dispose()
}
refreshDisposable = Observable.interval(30, TimeUnit.SECONDS)
@ -219,11 +216,7 @@ class ChatFragment : BaseFragment<FragmentChatBinding>() {
private fun sendChatMessage(chatText: String) {
viewModel?.postGroupChat(
chatText,
{
binding?.recyclerView?.scrollToPosition(0)
}
) {
binding?.chatBarView?.message = chatText
}
{ binding?.recyclerView?.scrollToPosition(0) }
) { binding?.chatBarView?.message = chatText }
}
}

View file

@ -6,7 +6,7 @@ import android.content.Context
import android.os.Bundle
import android.view.*
import androidx.appcompat.app.AlertDialog
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import com.habitrpg.android.habitica.MainNavDirections
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.components.UserComponent
@ -51,7 +51,9 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
private var chatRoomUser: String? = null
private var replyToUserUUID: String? = null
private var viewModel: InboxViewModel? = null
private val viewModel: InboxViewModel by viewModels(factoryProducer = {
InboxViewModelFactory(replyToUserUUID, chatRoomUser)
})
private var refreshDisposable: Disposable? = null
override fun onCreateView(
@ -71,7 +73,6 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
val args = InboxMessageListFragmentArgs.fromBundle(it)
setReceivingUser(args.username, args.userID)
}
viewModel = ViewModelProvider(this, InboxViewModelFactory(replyToUserUUID, chatRoomUser)).get(InboxViewModel::class.java)
val layoutManager = androidx.recyclerview.widget.LinearLayoutManager(this.getActivity())
layoutManager.reverseLayout = true
@ -88,9 +89,7 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
setReceivingUser(member.username, member.id)
activity?.title = member.displayName
chatAdapter = InboxAdapter(user, member)
viewModel?.messages?.observe(
this.viewLifecycleOwner
) {
viewModel.messages.observe(this.viewLifecycleOwner) {
markMessagesAsRead(it)
chatAdapter?.submitList(it)
}
@ -197,32 +196,32 @@ class InboxMessageListFragment : BaseMainFragment<FragmentInboxMessageListBindin
}
private fun refreshConversation() {
if (viewModel?.memberID?.isNotBlank() != true) { return }
if (viewModel.memberID?.isNotBlank() != true) { return }
compositeSubscription.add(
this.socialRepository.retrieveInboxMessages(replyToUserUUID ?: "", 0)
.subscribe(
{}, RxErrorHandler.handleEmptyError(),
{
viewModel?.invalidateDataSource()
viewModel.invalidateDataSource()
}
)
)
}
private fun sendMessage(chatText: String) {
viewModel?.memberID?.let { userID ->
viewModel.memberID?.let { userID ->
socialRepository.postPrivateMessage(userID, chatText)
.delay(200, TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
binding?.recyclerView?.scrollToPosition(0)
viewModel?.invalidateDataSource()
viewModel.invalidateDataSource()
},
{ error ->
RxErrorHandler.reportError(error)
if (binding != null) {
val alert = HabiticaAlertDialog(binding!!.chatBarView.context)
binding?.let {
val alert = HabiticaAlertDialog(it.chatBarView.context)
alert.setTitle("You cannot reply to this conversation")
alert.setMessage("This user is unable to receive your private message")
alert.addOkButton()

View file

@ -3,7 +3,7 @@ package com.habitrpg.android.habitica.ui.fragments.social
import android.os.Bundle
import android.view.*
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.google.android.material.tabs.TabLayoutMediator
import com.habitrpg.android.habitica.R
@ -27,10 +27,7 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
return FragmentViewpagerBinding.inflate(inflater, container, false)
}
internal lateinit var viewModel: GroupViewModel
internal var tavernDetailFragment = TavernDetailFragment()
private var chatFragment: ChatFragment? = null
internal val viewModel: GroupViewModel by viewModels()
override fun onCreateView(
inflater: LayoutInflater,
@ -48,7 +45,6 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(GroupViewModel::class.java)
viewModel.groupViewType = GroupViewType.GUILD
viewModel.setGroupID(Group.TAVERN_ID)
@ -90,9 +86,7 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
TavernDetailFragment()
}
1 -> {
chatFragment = ChatFragment()
chatFragment?.viewModel = viewModel
chatFragment ?: Fragment()
ChatFragment(viewModel)
}
else -> Fragment()
}

View file

@ -28,7 +28,7 @@ import com.habitrpg.android.habitica.ui.helpers.setMarkdown
import com.habitrpg.android.habitica.ui.viewmodels.GroupViewModel
import com.habitrpg.android.habitica.ui.views.HabiticaIcons
import com.habitrpg.android.habitica.ui.views.HabiticaIconsHelper
import com.habitrpg.android.habitica.ui.views.HabiticaSnackbar
import com.habitrpg.android.habitica.ui.views.SnackbarActivity
import com.habitrpg.android.habitica.ui.views.dialogs.HabiticaAlertDialog
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
@ -75,10 +75,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
}
binding?.joinButton?.setOnClickListener {
viewModel?.joinGroup {
val activity = activity as? MainActivity
if (activity != null) {
HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.joined_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL)
}
(this.activity as? SnackbarActivity)?.showSnackbar(title = getString(R.string.joined_guild))
}
}
binding?.inviteButton?.setOnClickListener {
@ -86,7 +83,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
sendInvitesResult.launch(intent)
}
binding?.leaderWrapper?.setOnClickListener {
viewModel?.getGroupData()?.value?.leaderID?.let { leaderID ->
viewModel?.leaderID?.let { leaderID ->
val profileDirections = MainNavDirections.openProfileActivity(leaderID)
MainNavigationController.navigate(profileDirections)
}
@ -98,7 +95,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
return
}
binding?.leaderAvatarView?.setAvatar(leader)
binding?.leaderProfileName?.username = leader.profile?.name
binding?.leaderProfileName?.username = leader.displayName
binding?.leaderProfileName?.tier = leader.contributor?.level ?: 0
binding?.leaderUsername?.text = leader.formattedUsername
}
@ -140,7 +137,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
}
private fun getGroupChallenges(): List<Challenge> {
var groupChallenges = mutableListOf<Challenge>()
val groupChallenges = mutableListOf<Challenge>()
userRepository.getUser(userId).forEach {
it.challenges?.forEach {
challengeRepository.getChallenge(it.challengeID).forEach {
@ -156,7 +153,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
internal fun leaveGuild() {
val context = context
if (context != null) {
var groupChallenges = getGroupChallenges()
val groupChallenges = getGroupChallenges()
lifecycleScope.launch(Dispatchers.Main) {
delay(500)
if (groupChallenges.isNotEmpty()) {
@ -188,10 +185,7 @@ class GuildDetailFragment : BaseFragment<FragmentGuildDetailBinding>() {
}
private fun showLeaveSnackbar() {
val activity = activity as? MainActivity
if (activity != null) {
HabiticaSnackbar.showSnackbar(activity.snackbarContainer, getString(R.string.left_guild), HabiticaSnackbar.SnackbarDisplayType.NORMAL)
}
(this.activity as? MainActivity)?.showSnackbar(title = getString(R.string.left_guild))
}
override fun injectFragment(component: UserComponent) {

View file

@ -6,7 +6,7 @@ import android.os.Bundle
import android.view.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
@ -23,7 +23,7 @@ import com.habitrpg.android.habitica.ui.viewmodels.GroupViewType
class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
internal lateinit var viewModel: GroupViewModel
internal val viewModel: GroupViewModel by viewModels()
private var guildInformationFragment: GuildDetailFragment? = null
private var chatFragment: ChatFragment? = null
@ -51,7 +51,6 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
showsBackButton = true
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this).get(GroupViewModel::class.java)
viewModel.groupViewType = GroupViewType.GUILD
viewModel.getGroupData().observe(viewLifecycleOwner, { setGroup(it) })
viewModel.getIsMemberData().observe(viewLifecycleOwner, { activity?.invalidateOptionsMenu() })
@ -95,10 +94,9 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
}
override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) {
val guild = viewModel.getGroupData().value
if (this.activity != null && guild != null) {
if (this.activity != null) {
if (viewModel.isMember) {
if (this.user != null && this.user?.id == guild.leaderID) {
if (viewModel.isLeader) {
this.activity?.menuInflater?.inflate(R.menu.guild_admin, menu)
} else {
this.activity?.menuInflater?.inflate(R.menu.guild_member, menu)
@ -219,7 +217,7 @@ class GuildFragment : BaseMainFragment<FragmentViewpagerBinding>() {
private fun setGroup(group: Group?) {
this.activity?.invalidateOptionsMenu()
if (group?.privacy == "public") {
if (viewModel.isPublicGuild) {
chatFragment?.autocompleteContext = "publicGuild"
} else {
chatFragment?.autocompleteContext = "privateGuild"

View file

@ -6,7 +6,7 @@ import android.os.Bundle
import android.view.*
import androidx.activity.result.contract.ActivityResultContracts
import androidx.fragment.app.Fragment
import androidx.lifecycle.ViewModelProvider
import androidx.fragment.app.viewModels
import androidx.viewpager2.adapter.FragmentStateAdapter
import androidx.viewpager2.widget.ViewPager2
import com.google.android.material.tabs.TabLayoutMediator
@ -28,7 +28,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
private var chatFragment: ChatFragment? = null
private var viewPagerAdapter: FragmentStateAdapter? = null
internal lateinit var viewModel: PartyViewModel
internal val viewModel: PartyViewModel by viewModels()
override var binding: FragmentViewpagerBinding? = null
@ -49,8 +49,6 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
viewModel = ViewModelProvider(this)
.get(PartyViewModel::class.java)
viewModel.groupViewType = GroupViewType.PARTY
viewModel.getGroupData().observe(

View file

@ -439,7 +439,7 @@ open class TaskRecyclerViewFragment : BaseFragment<FragmentRefreshRecyclerviewBi
when (taskType) {
TaskType.TODO -> taskFilterHelper.setActiveFilter(TaskType.TODO, Task.FILTER_ACTIVE)
TaskType.DAILY -> {
val user = (activity as? MainActivity)?.user
val user = (activity as? MainActivity)?.viewModel?.user?.value
if (user?.isValid == true && user.preferences?.dailyDueDefaultView == true) {
taskFilterHelper.setActiveFilter(TaskType.DAILY, Task.FILTER_ACTIVE)
}

View file

@ -15,6 +15,11 @@ import javax.inject.Inject
abstract class BaseViewModel(initializeComponent: Boolean = true) : ViewModel() {
val isUserFainted: Boolean
get() = (user.value?.stats?.hp ?: 1.0) == 0.0
val isUserInParty: Boolean
get() = user.value?.hasParty == true
@Inject
lateinit var userRepository: UserRepository
@ -42,7 +47,7 @@ abstract class BaseViewModel(initializeComponent: Boolean = true) : ViewModel()
internal val disposable = CompositeDisposable()
private fun loadUserFromLocal() {
internal fun loadUserFromLocal() {
disposable.add(userRepository.getUser().observeOn(AndroidSchedulers.mainThread())
.subscribe({ _user.value = it }, RxErrorHandler.handleEmptyError()))
}

View file

@ -109,6 +109,10 @@ open class GroupViewModel(initializeComponent: Boolean) : BaseViewModel(initiali
get() = isMemberData.value ?: false
val leaderID: String?
get() = group.value?.leaderID
val isLeader: Boolean
get() = user.value?.id == leaderID
val isPublicGuild: Boolean
get() = group.value?.privacy == "public"
fun getGroupData(): LiveData<Group?> = group
fun getLeaderData(): LiveData<Member?> = leader

View file

@ -0,0 +1,162 @@
package com.habitrpg.android.habitica.ui.viewmodels
import android.content.SharedPreferences
import androidx.core.content.edit
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.api.HostConfig
import com.habitrpg.android.habitica.api.MaintenanceApiService
import com.habitrpg.android.habitica.components.UserComponent
import com.habitrpg.android.habitica.data.ContentRepository
import com.habitrpg.android.habitica.data.InventoryRepository
import com.habitrpg.android.habitica.data.TaskRepository
import com.habitrpg.android.habitica.helpers.AmplitudeManager
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.helpers.TaskAlarmManager
import com.habitrpg.android.habitica.helpers.notifications.PushNotificationManager
import com.habitrpg.android.habitica.models.TutorialStep
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.responses.MaintenanceResponse
import com.habitrpg.android.habitica.proxy.AnalyticsManager
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import io.realm.kotlin.isValid
import java.util.*
import javax.inject.Inject
class MainActivityViewModel: BaseViewModel() {
@Inject
internal lateinit var hostConfig: HostConfig
@Inject
internal lateinit var pushNotificationManager: PushNotificationManager
@Inject
internal lateinit var sharedPreferences: SharedPreferences
@Inject
internal lateinit var contentRepository: ContentRepository
@Inject
internal lateinit var taskRepository: TaskRepository
@Inject
internal lateinit var inventoryRepository: InventoryRepository
@Inject
internal lateinit var taskAlarmManager: TaskAlarmManager
@Inject
internal lateinit var analyticsManager: AnalyticsManager
@Inject
internal lateinit var maintenanceService: MaintenanceApiService
override fun inject(component: UserComponent) {
component.inject(this)
}
val isAuthenticated: Boolean
get() = hostConfig.apiKey.isNotBlank() && hostConfig.userID.isNotBlank()
val launchScreen: String?
get() = sharedPreferences.getString("launch_screen", "")
var preferenceLanguage: String?
get() = sharedPreferences.getString("language", "en")
set(value) {
sharedPreferences.edit {
putString("language", value)
}
}
override fun onCleared() {
taskRepository.close()
inventoryRepository.close()
contentRepository.close()
super.onCleared()
}
fun onCreate() {
try {
taskAlarmManager.scheduleAllSavedAlarms(sharedPreferences.getBoolean("preventDailyReminder", false))
} catch (e: Exception) {
analyticsManager.logException(e)
}
}
fun onResume() {
// Track when the app was last opened, so that we can use this to send out special reminders after a week of inactivity
sharedPreferences.edit {
putLong("lastAppLaunch", Date().time)
putBoolean("preventDailyReminder", false)
}
}
fun retrieveUser(forced: Boolean = false) {
if (hostConfig.hasAuthentication()) {
disposable.add(
contentRepository.retrieveWorldState()
.flatMap { userRepository.retrieveUser(true, forced) }
.doOnNext { user1 ->
analyticsManager.setUserProperty("has_party", if (user1.party?.id?.isNotEmpty() == true) "true" else "false")
analyticsManager.setUserProperty("is_subscribed", if (user1.isSubscribed) "true" else "false")
analyticsManager.setUserProperty("checkin_count", user1.loginIncentives.toString())
analyticsManager.setUserProperty("level", user1.stats?.lvl?.toString() ?: "")
pushNotificationManager.setUser(user1)
pushNotificationManager.addPushDeviceUsingStoredToken()
}
.flatMap { userRepository.retrieveTeamPlans() }
.flatMap { contentRepository.retrieveContent() }
.subscribe({ }, RxErrorHandler.handleEmptyError())
)
}
}
fun logTutorialStatus(step: TutorialStep, complete: Boolean) {
val additionalData = HashMap<String, Any>()
additionalData["eventLabel"] = step.identifier + "-android"
additionalData["eventValue"] = step.identifier ?: ""
additionalData["complete"] = complete
AmplitudeManager.sendEvent(
"tutorial",
AmplitudeManager.EVENT_CATEGORY_BEHAVIOUR,
AmplitudeManager.EVENT_HITTYPE_EVENT,
additionalData
)
}
fun ifNeedsMaintenance(onResult: ((MaintenanceResponse) -> Unit)) {
disposable.add(
this.maintenanceService.maintenanceStatus
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe( { maintenanceResponse ->
if (maintenanceResponse == null) {
return@subscribe
}
onResult(maintenanceResponse)
},
RxErrorHandler.handleEmptyError()
)
)
}
fun getToolbarTitle(id: Int, label: CharSequence?, eggType: String?, onSuccess: ((CharSequence?) -> Unit)) {
if (id == R.id.petDetailRecyclerFragment || id == R.id.mountDetailRecyclerFragment) {
disposable.add(
inventoryRepository.getItem("egg", eggType ?: "").firstElement().subscribe(
{
if (!it.isValid()) return@subscribe
onSuccess(
if (id == R.id.petDetailRecyclerFragment) {
(it as? Egg)?.text
} else {
(it as? Egg)?.mountText
}
)
},
RxErrorHandler.handleEmptyError()
)
)
} else {
onSuccess(
if (id == R.id.promoInfoFragment) {
""
} else if (label.isNullOrEmpty() && user.value?.isValid == true) {
user.value?.profile?.name
} else label ?: ""
)
}
}
}

View file

@ -8,11 +8,14 @@ import android.widget.LinearLayout
import androidx.core.graphics.drawable.toBitmap
import com.habitrpg.android.habitica.R
import com.habitrpg.android.habitica.databinding.DialogPetSuggestHatchBinding
import com.habitrpg.android.habitica.extensions.subscribeWithErrorHandler
import com.habitrpg.android.habitica.helpers.RxErrorHandler
import com.habitrpg.android.habitica.interactors.HatchPetUseCase
import com.habitrpg.android.habitica.models.inventory.Animal
import com.habitrpg.android.habitica.models.inventory.Egg
import com.habitrpg.android.habitica.models.inventory.HatchingPotion
import com.habitrpg.android.habitica.models.inventory.Item
import com.habitrpg.android.habitica.ui.activities.BaseActivity
import com.habitrpg.android.habitica.ui.activities.MainActivity
import com.habitrpg.android.habitica.ui.helpers.DataBindingUtils
import com.habitrpg.android.habitica.ui.views.CurrencyView
@ -20,9 +23,13 @@ import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.core.Flowable
import io.reactivex.rxjava3.core.Observable
import java.util.*
import javax.inject.Inject
class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) {
@Inject
lateinit var hatchPetUseCase: HatchPetUseCase
private lateinit var binding: DialogPetSuggestHatchBinding
init {
@ -75,7 +82,7 @@ class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) {
addButton(R.string.hatch, true, false) { _, _ ->
val thisPotion = potion ?: return@addButton
val thisEgg = egg ?: return@addButton
(getActivity() as? MainActivity)?.hatchPet(thisPotion, thisEgg)
hatchPet(thisPotion, thisEgg)
}
if (hasMount) {
setTitle(R.string.hatch_your_pet)
@ -134,7 +141,7 @@ class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) {
.flatMap { activity.userRepository.retrieveUser(true, forced = true) }
.subscribe(
{
(getActivity() as? MainActivity)?.hatchPet(thisPotion, thisEgg)
hatchPet(thisPotion, thisEgg)
},
RxErrorHandler.handleEmptyError()
)
@ -159,6 +166,16 @@ class PetSuggestHatchDialog(context: Context) : HabiticaAlertDialog(context) {
}
}
private fun hatchPet(potion: HatchingPotion, egg: Egg) {
(getActivity() as? BaseActivity)?.let {
hatchPetUseCase.observable(
HatchPetUseCase.RequestValues(
potion, egg,
it
)).subscribeWithErrorHandler {}
}
}
private fun getItemPrice(pet: Animal, item: Item?, hasUnlocked: Boolean): Int {
if (pet.type == "drop" || (pet.type == "quest" && hasUnlocked)) {
return item?.value ?: 0

View file

@ -19,6 +19,10 @@ class AnalyticsManagerImpl(context: Context) : AnalyticsManager {
Amplitude.getInstance().userId = identifier
}
override fun setUserProperty(identifier: String, value: String) {
firebaseAnalytics.setUserProperty(identifier, value)
}
override fun logError(msg: String) {
FirebaseCrashlytics.getInstance().log(msg)
}