0
0
mirror of https://github.com/ankidroid/Anki-Android.git synced 2024-09-20 12:02:16 +02:00

feat: implement App Introduction System

* Displays the App Introduction System
* Handles only displaying it once
* Handles syncing if the app intro loads an existing AnkiWeb account
* (special case) Handles an upgrade from 2.15 -> 2.16 where
  the preference for the app intro was unset.
This commit is contained in:
David Allison 2022-08-15 23:58:53 +01:00 committed by Mike Hardy
parent b3a138035b
commit 529bc11db7
4 changed files with 49 additions and 2 deletions

View File

@ -149,6 +149,9 @@ import kotlin.math.roundToLong
* * A custom image as a background can be added: [applyDeckPickerBackground]
*/
@KotlinCleanup("lots to do")
@NeedsTest("On a new startup, the App Intro is displayed")
@NeedsTest("If the collection has been created, the app intro is not displayed")
@NeedsTest("If the user selects 'Sync Profile' in the app intro, a sync starts immediately")
open class DeckPicker :
NavigationDrawerActivity(),
StudyOptionsListener,
@ -382,6 +385,19 @@ open class DeckPicker :
// Then set theme and content view
super.onCreate(savedInstanceState)
// handle the first load: display the app introduction
if (!hasShownAppIntro()) {
val appIntro = Intent(this, IntroductionActivity::class.java)
appIntro.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
startActivityWithoutAnimation(appIntro)
finish() // calls onDestroy() immediately
return
}
if (intent.hasExtra(INTENT_SYNC_FROM_LOGIN)) {
mSyncOnResume = true
}
setContentView(R.layout.homescreen)
handleStartup()
val mainView = findViewById<View>(android.R.id.content)
@ -463,6 +479,23 @@ open class DeckPicker :
Onboarding.DeckPicker(this, mRecyclerViewLayoutManager).onCreate()
}
private fun hasShownAppIntro(): Boolean {
val prefs = AnkiDroidApp.getSharedPrefs(this)
// if moving from 2.15 to 2.16 then we do not want to show the intro
// remove this after ~2.17 and default to 'false' if the pref is not set
if (!prefs.contains(IntroductionActivity.INTRODUCTION_SLIDES_SHOWN)) {
return if (!InitialActivity.wasFreshInstall(prefs)) {
prefs.edit { putBoolean(IntroductionActivity.INTRODUCTION_SLIDES_SHOWN, true) }
true
} else {
false
}
}
return prefs.getBoolean(IntroductionActivity.INTRODUCTION_SLIDES_SHOWN, false)
}
/**
* The first call in showing dialogs for startup - error or success.
* Attempts startup if storage permission has been acquired, else, it requests the permission
@ -861,7 +894,8 @@ open class DeckPicker :
fun refreshState() {
mActivityPaused = false
if (mSyncOnResume) {
// Due to the App Introduction, this may be called before permission has been granted.
if (mSyncOnResume && hasStorageAccessPermission(this)) {
Timber.i("Performing Sync on Resume")
sync()
mSyncOnResume = false
@ -2684,6 +2718,14 @@ open class DeckPicker :
const val RESULT_DB_ERROR = 203
const val UPGRADE_VERSION_KEY = "lastUpgradeVersion"
/**
* If passed into the intent, the user should have been logged in and DeckPicker
* should sync immediately.
*
* This is for the 'download existing collection from AnkiWeb' use case
*/
const val INTENT_SYNC_FROM_LOGIN = "syncFromLogin"
/**
* Available options performed by other activities (request codes for onActivityResult())
*/

View File

@ -96,7 +96,7 @@ class IntroductionActivity : AppIntro() {
val deckPicker = Intent(this, DeckPicker::class.java)
deckPicker.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
if (result == RESULT_SYNC_PROFILE) {
deckPicker.putExtra("syncFromLogin", true)
deckPicker.putExtra(DeckPicker.INTENT_SYNC_FROM_LOGIN, true)
}
startActivity(deckPicker)

View File

@ -5,6 +5,7 @@ import android.content.Intent
import android.content.SharedPreferences
import android.content.pm.PackageManager
import android.view.Menu
import androidx.core.content.edit
import androidx.test.core.app.ActivityScenario
import com.ichi2.anki.dialogs.DatabaseErrorDialog
import com.ichi2.anki.dialogs.DeckPickerConfirmDeleteDeckDialog
@ -54,6 +55,7 @@ class DeckPickerTest : RobolectricTest() {
@Before
fun before() {
RuntimeEnvironment.setQualifiers(mQualifiers)
getPreferences().edit { putBoolean(IntroductionActivity.INTRODUCTION_SLIDES_SHOWN, true) }
}
@Test

View File

@ -17,6 +17,7 @@
package com.ichi2.anki.dialogs
import android.widget.EditText
import androidx.core.content.edit
import androidx.lifecycle.Lifecycle
import androidx.test.core.app.ActivityScenario
import com.afollestad.materialdialogs.WhichButton
@ -24,6 +25,7 @@ import com.afollestad.materialdialogs.actions.getActionButton
import com.afollestad.materialdialogs.input.getInputField
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.DeckPicker
import com.ichi2.anki.IntroductionActivity
import com.ichi2.anki.R
import com.ichi2.anki.RobolectricTest
import com.ichi2.libanki.DeckManager
@ -51,6 +53,7 @@ class CreateDeckDialogTest : RobolectricTest() {
private var mActivityScenario: ActivityScenario<DeckPicker>? = null
override fun setUp() {
super.setUp()
getPreferences().edit { putBoolean(IntroductionActivity.INTRODUCTION_SLIDES_SHOWN, true) }
ensureCollectionLoadIsSynchronous()
mActivityScenario = ActivityScenario.launch(DeckPicker::class.java)
val activityScenario: ActivityScenario<DeckPicker>? = mActivityScenario