Skip to content

Commit

Permalink
fix: attach custom study dialog fragment factory in superclass to avo…
Browse files Browse the repository at this point in the history
…id crash

There were two Activities that attached the factory in onCreate, but there were *three*
that needed it (SinglePageActivity hosting CongratsPage fragment hosting study options
was missing)

If you didn't attach in onCreate then you would crash if the fragment/activity lifecycle
had done a cycle on you due to background kill or don't keep activities on
  • Loading branch information
mikehardy committed Nov 27, 2024
1 parent 0fe5e9b commit 4e57832
Show file tree
Hide file tree
Showing 3 changed files with 38 additions and 16 deletions.
43 changes: 38 additions & 5 deletions AnkiDroid/src/main/java/com/ichi2/anki/AnkiActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,8 @@ import com.ichi2.anki.dialogs.AsyncDialogFragment
import com.ichi2.anki.dialogs.DialogHandler
import com.ichi2.anki.dialogs.SimpleMessageDialog
import com.ichi2.anki.dialogs.SimpleMessageDialog.SimpleMessageDialogListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog.CustomStudyListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory
import com.ichi2.anki.preferences.Preferences
import com.ichi2.anki.preferences.Preferences.Companion.MINIMUM_CARDS_DUE_FOR_NOTIFICATION
import com.ichi2.anki.preferences.sharedPrefs
Expand All @@ -73,13 +75,19 @@ import com.ichi2.compat.customtabs.CustomTabsHelper
import com.ichi2.libanki.Collection
import com.ichi2.themes.Themes
import com.ichi2.utils.AdaptionUtil
import com.ichi2.utils.ExtendedFragmentFactory
import com.ichi2.utils.KotlinCleanup
import timber.log.Timber
import androidx.browser.customtabs.CustomTabsIntent.Builder as CustomTabsIntentBuilder

@UiThread
@KotlinCleanup("set activityName")
open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, ShortcutGroupProvider, AnkiActivityProvider {
open class AnkiActivity :
AppCompatActivity,
CustomStudyListener,
SimpleMessageDialogListener,
ShortcutGroupProvider,
AnkiActivityProvider {

/**
* Receiver that informs us when a broadcast listen in [broadcastsActions] is received.
Expand Down Expand Up @@ -111,6 +119,16 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, Shortc
// Set the theme
Themes.setTheme(this)
Themes.disableXiaomiForceDarkMode(this)

// We may use the CustomStudyDialog in:
// - SinglePageActivity (CongratsPage fragment..)
// - StudyOptionsActivity
// - DeckPicker
// - anyone could add it, and it will crash when restoring from background unless
// our custom fragment factory is added first, so add it in superclass
val customStudyDialogFactory = CustomStudyDialogFactory({ this.getColUnsafe }, this)
customStudyDialogFactory.attachToActivity<ExtendedFragmentFactory>(this)

super.onCreate(savedInstanceState)
// Disable the notifications bar if running under the test monkey.
if (AdaptionUtil.isUserATestClient) {
Expand Down Expand Up @@ -375,20 +393,35 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, Shortc
startActivity(deckPicker)
}

fun showProgressBar() {
override fun showProgressBar() {
val progressBar = findViewById<ProgressBar>(R.id.progress_bar)
if (progressBar != null) {
progressBar.visibility = View.VISIBLE
}
}

open fun hideProgressBar() {
override fun hideProgressBar() {
val progressBar = findViewById<ProgressBar>(R.id.progress_bar)
if (progressBar != null) {
progressBar.visibility = View.GONE
}
}

override fun onCreateCustomStudySession() {
// Activities that host a StudyDialogFragment should implement this
// guide developers to the solution with helpful information on where
Timber.w("onCreateCustomStudySession unimplemented - almost certainly not what you want")
Timber.w("if your Activity hosts CustomStudyDialog you need to override this method")
Timber.w(Exception("stack trace for reference"))
}
override fun onExtendStudyLimits() {
// Activities that host a StudyDialogFragment should implement this
// guide developers to the solution with helpful information on where
Timber.w("onExtendStudyLimits unimplemented - almost certainly not what you want")
Timber.w("if your Activity hosts CustomStudyDialog you need to override this method")
Timber.w(Exception("stack trace for reference"))
}

internal fun mayOpenUrl(url: Uri) {
val success = customTabActivityHelper.mayLaunchUrl(url, null, null)
if (!success) {
Expand Down Expand Up @@ -457,7 +490,7 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, Shortc
*
* @param newFragment the DialogFragment you want to show
*/
open fun showDialogFragment(newFragment: DialogFragment) {
override fun showDialogFragment(newFragment: DialogFragment) {
runOnUiThread {
showDialogFragment(this, newFragment)
}
Expand Down Expand Up @@ -579,7 +612,7 @@ open class AnkiActivity : AppCompatActivity, SimpleMessageDialogListener, Shortc
}

// Dismiss whatever dialog is showing
fun dismissAllDialogFragments() {
override fun dismissAllDialogFragments() {
// trying to pop fragment manager back state crashes if state already saved
if (!supportFragmentManager.isStateSaved) {
supportFragmentManager.popBackStack(
Expand Down
5 changes: 0 additions & 5 deletions AnkiDroid/src/main/java/com/ichi2/anki/DeckPicker.kt
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,6 @@ import com.ichi2.anki.dialogs.SyncErrorDialog
import com.ichi2.anki.dialogs.SyncErrorDialog.Companion.newInstance
import com.ichi2.anki.dialogs.SyncErrorDialog.SyncErrorDialogListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog.CustomStudyListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory
import com.ichi2.anki.export.ActivityExportingDelegate
import com.ichi2.anki.export.ExportDialogFragment
import com.ichi2.anki.export.ExportDialogsFactory
Expand Down Expand Up @@ -239,7 +237,6 @@ open class DeckPicker :
ImportDialogListener,
MediaCheckDialogListener,
OnRequestPermissionsResultCallback,
CustomStudyListener,
ChangeManager.Subscriber,
SyncCompletionListener,
ImportColpkgListener,
Expand Down Expand Up @@ -326,7 +323,6 @@ open class DeckPicker :

private var toolbarSearchItem: MenuItem? = null
private var toolbarSearchView: AccessibleSearchView? = null
private lateinit var customStudyDialogFactory: CustomStudyDialogFactory

override val permissionScreenLauncher = recreateActivityResultLauncher()

Expand Down Expand Up @@ -471,7 +467,6 @@ open class DeckPicker :
return
}
exportingDelegate = ActivityExportingDelegate(this) { getColUnsafe }
customStudyDialogFactory = CustomStudyDialogFactory({ getColUnsafe }, this).attachToActivity(this)

// Then set theme and content view
super.onCreate(savedInstanceState)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,19 +25,15 @@ import androidx.lifecycle.lifecycleScope
import anki.collection.OpChanges
import com.ichi2.anki.CollectionManager.withCol
import com.ichi2.anki.StudyOptionsFragment.StudyOptionsListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialog.CustomStudyListener
import com.ichi2.anki.dialogs.customstudy.CustomStudyDialogFactory
import com.ichi2.libanki.ChangeManager
import com.ichi2.ui.RtlCompliantActionProvider
import com.ichi2.utils.ExtendedFragmentFactory
import com.ichi2.widget.WidgetStatus
import kotlinx.coroutines.launch
import timber.log.Timber

class StudyOptionsActivity :
AnkiActivity(),
StudyOptionsListener,
CustomStudyListener,
ChangeManager.Subscriber {

private var undoState = UndoState()
Expand All @@ -46,8 +42,6 @@ class StudyOptionsActivity :
if (showedActivityFailedScreen(savedInstanceState)) {
return
}
val customStudyDialogFactory = CustomStudyDialogFactory({ this.getColUnsafe }, this)
customStudyDialogFactory.attachToActivity<ExtendedFragmentFactory>(this)
super.onCreate(savedInstanceState)
setContentView(R.layout.studyoptions)
enableToolbar().apply { title = "" }
Expand Down

0 comments on commit 4e57832

Please sign in to comment.