This commit is contained in:
Phillip Thelen 2022-04-04 14:12:35 +02:00
parent 100966cdfa
commit 8dc433e7bf
14 changed files with 59 additions and 106 deletions

View file

@ -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
}

View file

@ -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() }
)
}

View file

@ -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"
}

View file

@ -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)
}
}

View file

@ -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) {

View file

@ -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()

View file

@ -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()
)
)
}
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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)
}

View file

@ -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 {}
}

View file

@ -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
}
}
}

View file

@ -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()
)

View file

@ -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"