diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt index 18b14bb99..34239a0e5 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/implementation/UserRepositoryImpl.kt @@ -106,7 +106,7 @@ class UserRepositoryImpl(localRepository: UserLocalRepository, apiClient: ApiCli .map> { tutorialSteps -> val updateData = HashMap() for (step in tutorialSteps) { - updateData["flags.tutorial." + step.tutorialGroup + "." + step.identifier] = false + updateData[step.flagPath] = false } updateData } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTutorialLocalRepository.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTutorialLocalRepository.kt index 8ddadb7c8..8f7577680 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTutorialLocalRepository.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/data/local/implementation/RealmTutorialLocalRepository.kt @@ -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() - list.add(step) - list - } else { - steps - } - } .map { steps -> steps.first() } ) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/models/TutorialStep.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/models/TutorialStep.kt index 316234d45..af6369309 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/models/TutorialStep.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/models/TutorialStep.kt @@ -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 get() = TutorialStep::class.java override val primaryIdentifier: String? get() = key override val primaryIdentifierName: String get() = "key" + + val flagPath: String + get() = "flags.tutorial.$tutorialGroup.$identifier" } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/TutorialView.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/TutorialView.kt index 91dfffe84..7708b2c62 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/TutorialView.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/TutorialView.kt @@ -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 = emptyList() private var currentTextIndex: Int = 0 @@ -35,6 +35,10 @@ class TutorialView(context: Context, var step: TutorialStep, var onReaction: OnT } fun setTutorialTexts(texts: List) { + 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) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt index bb8224e9f..b0fcd6108 100755 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/activities/MainActivity.kt @@ -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, 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) { diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseDialogFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseDialogFragment.kt index 81363c4b3..269648cfb 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseDialogFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseDialogFragment.kt @@ -27,7 +27,6 @@ abstract class BaseDialogFragment : DialogFragment() { lateinit var tutorialRepository: TutorialRepository var tutorialStepIdentifier: String? = null - var tutorialText: String? = null protected var tutorialCanBeDeferred = true var tutorialTexts: MutableList = ArrayList() @@ -72,13 +71,9 @@ abstract class BaseDialogFragment : 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() diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt index d7099c89e..5291f2921 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/BaseFragment.kt @@ -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 : Fragment() { lateinit var analyticsManager: AnalyticsManager var tutorialStepIdentifier: String? = null - var tutorialText: String? = null protected var tutorialCanBeDeferred = true - var tutorialTexts: MutableList = ArrayList() + var tutorialTexts: List = ArrayList() protected var compositeSubscription: CompositeDisposable = CompositeDisposable() @@ -69,27 +67,21 @@ abstract class BaseFragment : 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() + ) + ) } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt index 498ef814d..113199e72 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/StatsFragment.kt @@ -68,7 +68,7 @@ class StatsFragment : BaseMainFragment() { 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) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt index 34c8bb487..317515d1f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/skills/SkillsFragment.kt @@ -50,7 +50,7 @@ class SkillsFragment : BaseMainFragment() { 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) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/TavernFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/TavernFragment.kt index 79c21b789..08a7e9245 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/TavernFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/TavernFragment.kt @@ -42,7 +42,7 @@ class TavernFragment : BaseMainFragment() { 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) } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt index 4178d67dd..cd73af98f 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/social/party/PartyFragment.kt @@ -77,7 +77,7 @@ class PartyFragment : BaseMainFragment() { viewModel.loadPartyID() this.tutorialStepIdentifier = "party" - this.tutorialText = getString(R.string.tutorial_party) + this.tutorialTexts = listOf(getString(R.string.tutorial_party)) viewModel.retrieveGroup {} } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt index 488d2d9b7..2101f0e82 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TasksFragment.kt @@ -329,7 +329,7 @@ class TasksFragment : BaseMainFragment(), 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 } } } diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt index 6117b5b96..b356f15cc 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/fragments/tasks/TeamBoardFragment.kt @@ -328,15 +328,6 @@ class TeamBoardFragment : BaseMainFragment(), 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() ) diff --git a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt index dadb70525..5e3c3db1d 100644 --- a/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt +++ b/Habitica/src/main/java/com/habitrpg/android/habitica/ui/viewmodels/MainActivityViewModel.kt @@ -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() additionalData["eventLabel"] = step.identifier + "-android"