mirror of
https://github.com/sudoxnym/habitica-android.git
synced 2026-05-19 20:29:02 +00:00
Fixes #1626
This commit is contained in:
parent
100966cdfa
commit
8dc433e7bf
14 changed files with 59 additions and 106 deletions
|
|
@ -106,7 +106,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli
|
|||
.map<Map<String, Any>> { tutorialSteps ->
|
||||
val updateData = HashMap<String, Any>()
|
||||
for (step in tutorialSteps) {
|
||||
updateData["flags.tutorial." + step.tutorialGroup + "." + step.identifier] = false
|
||||
updateData[step.flagPath] = false
|
||||
}
|
||||
updateData
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,17 +15,6 @@ class RealmTutorialLocalRepository(realm: Realm) : RealmBaseLocalRepository(real
|
|||
.findAll()
|
||||
.asFlowable()
|
||||
.filter { realmObject -> realmObject.isLoaded && realmObject.isValid && realmObject.isNotEmpty() }
|
||||
.map { steps ->
|
||||
return@map if (steps.isEmpty()) {
|
||||
val step = TutorialStep()
|
||||
step.identifier = key
|
||||
val list = ArrayList<TutorialStep>()
|
||||
list.add(step)
|
||||
list
|
||||
} else {
|
||||
steps
|
||||
}
|
||||
}
|
||||
.map { steps -> steps.first() }
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,13 +22,18 @@ open class TutorialStep : RealmObject(), BaseMainObject {
|
|||
var wasCompleted: Boolean = false
|
||||
var displayedOn: Date? = null
|
||||
|
||||
fun shouldDisplay(): Boolean =
|
||||
!this.wasCompleted && (this.displayedOn == null || Date().time - (displayedOn?.time ?: 0) > 86400000)
|
||||
|
||||
val shouldDisplay: Boolean
|
||||
get() {
|
||||
return !this.wasCompleted && (this.displayedOn == null || Date().time - (displayedOn?.time
|
||||
?: 0) > 86400000)
|
||||
}
|
||||
override val realmClass: Class<out RealmModel>
|
||||
get() = TutorialStep::class.java
|
||||
override val primaryIdentifier: String?
|
||||
get() = key
|
||||
override val primaryIdentifierName: String
|
||||
get() = "key"
|
||||
|
||||
val flagPath: String
|
||||
get() = "flags.tutorial.$tutorialGroup.$identifier"
|
||||
}
|
||||
|
|
|
|||
|
|
@ -8,7 +8,7 @@ import com.habitrpg.android.habitica.databinding.OverlayTutorialBinding
|
|||
import com.habitrpg.android.habitica.extensions.layoutInflater
|
||||
import com.habitrpg.android.habitica.models.TutorialStep
|
||||
|
||||
class TutorialView(context: Context, var step: TutorialStep, var onReaction: OnTutorialReaction?) : FrameLayout(context) {
|
||||
class TutorialView(context: Context, val step: TutorialStep, private val onReaction: OnTutorialReaction) : FrameLayout(context) {
|
||||
private val binding = OverlayTutorialBinding.inflate(context.layoutInflater, this, true)
|
||||
private var tutorialTexts: List<String> = emptyList()
|
||||
private var currentTextIndex: Int = 0
|
||||
|
|
@ -35,6 +35,10 @@ class TutorialView(context: Context, var step: TutorialStep, var onReaction: OnT
|
|||
}
|
||||
|
||||
fun setTutorialTexts(texts: List<String>) {
|
||||
if (texts.size == 1) {
|
||||
setTutorialText(texts.first())
|
||||
return
|
||||
}
|
||||
tutorialTexts = texts
|
||||
currentTextIndex = -1
|
||||
displayNextTutorialText()
|
||||
|
|
@ -55,17 +59,17 @@ class TutorialView(context: Context, var step: TutorialStep, var onReaction: OnT
|
|||
binding.speechBubbleView.setHasMoreContent(true)
|
||||
}
|
||||
} else {
|
||||
this.onReaction?.onTutorialCompleted(this.step)
|
||||
onReaction.onTutorialCompleted(step)
|
||||
}
|
||||
}
|
||||
|
||||
private fun completeButtonClicked() {
|
||||
this.onReaction?.onTutorialCompleted(this.step)
|
||||
onReaction.onTutorialCompleted(step)
|
||||
(parent as? ViewGroup)?.removeView(this)
|
||||
}
|
||||
|
||||
private fun dismissButtonClicked() {
|
||||
this.onReaction?.onTutorialDeferred(this.step)
|
||||
onReaction.onTutorialDeferred(step)
|
||||
(parent as? ViewGroup)?.removeView(this)
|
||||
}
|
||||
|
||||
|
|
@ -75,7 +79,6 @@ class TutorialView(context: Context, var step: TutorialStep, var onReaction: OnT
|
|||
|
||||
interface OnTutorialReaction {
|
||||
fun onTutorialCompleted(step: TutorialStep)
|
||||
|
||||
fun onTutorialDeferred(step: TutorialStep)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import android.view.View
|
|||
import android.view.ViewGroup
|
||||
import androidx.activity.viewModels
|
||||
import androidx.appcompat.app.ActionBarDrawerToggle
|
||||
import androidx.core.view.children
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.navigation.NavDestination
|
||||
import androidx.navigation.findNavController
|
||||
|
|
@ -64,11 +65,10 @@ 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 java.util.Date
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, SnackbarActivity {
|
||||
open class MainActivity : BaseActivity(), SnackbarActivity {
|
||||
private var launchScreen: String? = null
|
||||
|
||||
@Inject
|
||||
|
|
@ -98,7 +98,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
|
|||
val viewModel: MainActivityViewModel by viewModels()
|
||||
private var faintDialog: HabiticaAlertDialog? = null
|
||||
private var sideAvatarView: AvatarView? = null
|
||||
private var activeTutorialView: TutorialView? = null
|
||||
private var drawerFragment: NavigationDrawerFragment? = null
|
||||
var drawerToggle: ActionBarDrawerToggle? = null
|
||||
private var resumeFromActivity = false
|
||||
|
|
@ -397,9 +396,6 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
|
|||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (this.activeTutorialView != null) {
|
||||
this.removeActiveTutorialView()
|
||||
}
|
||||
if (drawerFragment?.isDrawerOpen == true) {
|
||||
drawerFragment?.closeDrawer()
|
||||
} else {
|
||||
|
|
@ -479,47 +475,19 @@ open class MainActivity : BaseActivity(), TutorialView.OnTutorialReaction, Snack
|
|||
viewModel.retrieveUser(forced)
|
||||
}
|
||||
|
||||
fun displayTutorialStep(step: TutorialStep, text: String, canBeDeferred: Boolean) {
|
||||
removeActiveTutorialView()
|
||||
val view = TutorialView(this, step, this)
|
||||
this.activeTutorialView = view
|
||||
view.setTutorialText(text)
|
||||
view.onReaction = this
|
||||
view.setCanBeDeferred(canBeDeferred)
|
||||
binding.overlayFrameLayout.addView(view)
|
||||
viewModel.logTutorialStatus(step, false)
|
||||
}
|
||||
|
||||
fun displayTutorialStep(step: TutorialStep, texts: List<String>, canBeDeferred: Boolean) {
|
||||
removeActiveTutorialView()
|
||||
val view = TutorialView(this, step, this)
|
||||
this.activeTutorialView = view
|
||||
val view = TutorialView(this, step, viewModel)
|
||||
view.setTutorialTexts(texts)
|
||||
view.onReaction = this
|
||||
view.setCanBeDeferred(canBeDeferred)
|
||||
binding.overlayFrameLayout.children.forEach {
|
||||
if (it is TutorialView) {
|
||||
binding.overlayFrameLayout.removeView(it)
|
||||
}
|
||||
}
|
||||
binding.overlayFrameLayout.addView(view)
|
||||
viewModel.logTutorialStatus(step, false)
|
||||
}
|
||||
|
||||
override fun onTutorialCompleted(step: TutorialStep) {
|
||||
viewModel.updateUser("flags.tutorial." + step.tutorialGroup + "." + step.identifier, true)
|
||||
binding.overlayFrameLayout.removeView(this.activeTutorialView)
|
||||
this.removeActiveTutorialView()
|
||||
viewModel.logTutorialStatus(step, true)
|
||||
}
|
||||
|
||||
override fun onTutorialDeferred(step: TutorialStep) {
|
||||
taskRepository.modify(step) { it.displayedOn = Date() }
|
||||
this.removeActiveTutorialView()
|
||||
}
|
||||
|
||||
private fun removeActiveTutorialView() {
|
||||
if (this.activeTutorialView != null) {
|
||||
binding.overlayFrameLayout.removeView(this.activeTutorialView)
|
||||
this.activeTutorialView = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkMaintenance() {
|
||||
viewModel.ifNeedsMaintenance { maintenanceResponse ->
|
||||
if (maintenanceResponse.activeMaintenance) {
|
||||
|
|
|
|||
|
|
@ -27,7 +27,6 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() {
|
|||
lateinit var tutorialRepository: TutorialRepository
|
||||
|
||||
var tutorialStepIdentifier: String? = null
|
||||
var tutorialText: String? = null
|
||||
protected var tutorialCanBeDeferred = true
|
||||
var tutorialTexts: MutableList<String> = ArrayList()
|
||||
|
||||
|
|
@ -72,13 +71,9 @@ abstract class BaseDialogFragment<VB : ViewBinding> : DialogFragment() {
|
|||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
Consumer { step ->
|
||||
if (step != null && step.isValid && step.isManaged && step.shouldDisplay()) {
|
||||
if (step.isValid && step.isManaged && step.shouldDisplay()) {
|
||||
val mainActivity = activity as? MainActivity ?: return@Consumer
|
||||
if (tutorialText != null) {
|
||||
mainActivity.displayTutorialStep(step, tutorialText ?: "", tutorialCanBeDeferred)
|
||||
} else {
|
||||
mainActivity.displayTutorialStep(step, tutorialTexts, tutorialCanBeDeferred)
|
||||
}
|
||||
mainActivity.displayTutorialStep(step, tutorialTexts, tutorialCanBeDeferred)
|
||||
}
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
|
|
|
|||
|
|
@ -14,7 +14,6 @@ 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
|
||||
import io.reactivex.rxjava3.functions.Consumer
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Inject
|
||||
|
||||
|
|
@ -29,9 +28,8 @@ abstract class BaseFragment<VB : ViewBinding> : Fragment() {
|
|||
lateinit var analyticsManager: AnalyticsManager
|
||||
|
||||
var tutorialStepIdentifier: String? = null
|
||||
var tutorialText: String? = null
|
||||
protected var tutorialCanBeDeferred = true
|
||||
var tutorialTexts: MutableList<String> = ArrayList()
|
||||
var tutorialTexts: List<String> = ArrayList()
|
||||
|
||||
protected var compositeSubscription: CompositeDisposable = CompositeDisposable()
|
||||
|
||||
|
|
@ -69,27 +67,21 @@ abstract class BaseFragment<VB : ViewBinding> : Fragment() {
|
|||
}
|
||||
|
||||
private fun showTutorialIfNeeded() {
|
||||
if (view != null) {
|
||||
if (this.tutorialStepIdentifier != null) {
|
||||
compositeSubscription.add(
|
||||
tutorialRepository.getTutorialStep(this.tutorialStepIdentifier ?: "").firstElement()
|
||||
.delay(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(
|
||||
Consumer { step ->
|
||||
if (step != null && step.isValid && step.isManaged && step.shouldDisplay()) {
|
||||
val mainActivity = activity as? MainActivity ?: return@Consumer
|
||||
if (tutorialText != null) {
|
||||
mainActivity.displayTutorialStep(step, tutorialText ?: "", tutorialCanBeDeferred)
|
||||
} else {
|
||||
mainActivity.displayTutorialStep(step, tutorialTexts, tutorialCanBeDeferred)
|
||||
}
|
||||
}
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
)
|
||||
}
|
||||
tutorialStepIdentifier?.let { identifier ->
|
||||
compositeSubscription.add(
|
||||
tutorialRepository.getTutorialStep(identifier)
|
||||
.firstElement()
|
||||
.delay(1, TimeUnit.SECONDS)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ step ->
|
||||
if (step.isValid && step.isManaged && step.shouldDisplay) {
|
||||
val mainActivity = activity as? MainActivity ?: return@subscribe
|
||||
mainActivity.displayTutorialStep(step, tutorialTexts, tutorialCanBeDeferred)
|
||||
}
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ class StatsFragment : BaseMainFragment<FragmentStatsBinding>() {
|
|||
savedInstanceState: Bundle?
|
||||
): View? {
|
||||
tutorialStepIdentifier = "stats"
|
||||
tutorialText = getString(R.string.tutorial_stats)
|
||||
tutorialTexts = listOf(getString(R.string.tutorial_stats))
|
||||
this.hidesToolbar = true
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ class SkillsFragment : BaseMainFragment<FragmentSkillsBinding>() {
|
|||
adapter?.useSkillEvents?.subscribeWithErrorHandler { onSkillSelected(it) }?.let { compositeSubscription.add(it) }
|
||||
|
||||
this.tutorialStepIdentifier = "skills"
|
||||
this.tutorialText = getString(R.string.tutorial_skills)
|
||||
this.tutorialTexts = listOf(getString(R.string.tutorial_skills))
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,7 +42,7 @@ class TavernFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
this.usesTabLayout = true
|
||||
this.hidesToolbar = true
|
||||
this.tutorialStepIdentifier = "tavern"
|
||||
this.tutorialText = getString(R.string.tutorial_tavern)
|
||||
this.tutorialTexts = listOf(getString(R.string.tutorial_tavern))
|
||||
|
||||
return super.onCreateView(inflater, container, savedInstanceState)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ class PartyFragment : BaseMainFragment<FragmentViewpagerBinding>() {
|
|||
viewModel.loadPartyID()
|
||||
|
||||
this.tutorialStepIdentifier = "party"
|
||||
this.tutorialText = getString(R.string.tutorial_party)
|
||||
this.tutorialTexts = listOf(getString(R.string.tutorial_party))
|
||||
|
||||
viewModel.retrieveGroup {}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -329,7 +329,7 @@ class TasksFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchView.O
|
|||
if (fragment?.tutorialTexts != null && context != null) {
|
||||
val finalText = context?.getString(R.string.tutorial_tasks_complete)
|
||||
if (!fragment.tutorialTexts.contains(finalText) && finalText != null) {
|
||||
fragment.tutorialTexts.add(finalText)
|
||||
fragment.tutorialTexts = fragment.tutorialTexts + finalText
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -328,15 +328,6 @@ class TeamBoardFragment : BaseMainFragment<FragmentViewpagerBinding>(), SearchVi
|
|||
tab?.badgeCount = 0
|
||||
}
|
||||
}
|
||||
if (activeTutorialFragments.size == 1) {
|
||||
val fragment = viewFragmentsDictionary?.get(indexForTaskType(activeTutorialFragments[0]))
|
||||
if (fragment?.tutorialTexts != null && context != null) {
|
||||
val finalText = context?.getString(R.string.tutorial_tasks_complete)
|
||||
if (!fragment.tutorialTexts.contains(finalText) && finalText != null) {
|
||||
fragment.tutorialTexts.add(finalText)
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
RxErrorHandler.handleEmptyError()
|
||||
)
|
||||
|
|
|
|||
|
|
@ -17,13 +17,14 @@ 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 com.habitrpg.android.habitica.ui.TutorialView
|
||||
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.rxjava3.schedulers.Schedulers
|
||||
import io.realm.kotlin.isValid
|
||||
import java.util.Date
|
||||
import javax.inject.Inject
|
||||
|
||||
class MainActivityViewModel : BaseViewModel() {
|
||||
class MainActivityViewModel : BaseViewModel(), TutorialView.OnTutorialReaction {
|
||||
@Inject
|
||||
internal lateinit var hostConfig: HostConfig
|
||||
@Inject
|
||||
|
|
@ -102,6 +103,15 @@ class MainActivityViewModel : BaseViewModel() {
|
|||
}
|
||||
}
|
||||
|
||||
override fun onTutorialCompleted(step: TutorialStep) {
|
||||
updateUser("flags.tutorial." + step.tutorialGroup + "." + step.identifier, true)
|
||||
logTutorialStatus(step, true)
|
||||
}
|
||||
|
||||
override fun onTutorialDeferred(step: TutorialStep) {
|
||||
taskRepository.modify(step) { it.displayedOn = Date() }
|
||||
}
|
||||
|
||||
fun logTutorialStatus(step: TutorialStep, complete: Boolean) {
|
||||
val additionalData = HashMap<String, Any>()
|
||||
additionalData["eventLabel"] = step.identifier + "-android"
|
||||
|
|
|
|||
Loading…
Reference in a new issue