Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

java.lang.NoSuchMethodException: org.readium.r2.navigator.epub.EpubNavigatorFragment.<init> [] #395

Closed
RankoR opened this issue Sep 24, 2023 · 20 comments
Labels
bug Something isn't working

Comments

@RankoR
Copy link

RankoR commented Sep 24, 2023

Bug Report

What happened?

Android application crashes, probably on configuration changes (the easiest way to reproduce is to rotate the screen).

Orientation changes are disabled in my app, but as I see from Crashlytics it still crashes, probably on some other config changes.

Caused by java.lang.NoSuchMethodException: org.readium.r2.navigator.epub.EpubNavigatorFragment.<init> []
       at java.lang.Class.getConstructor0(Class.java:2332)
       at java.lang.Class.getConstructor(Class.java:1728)
       at androidx.fragment.app.Fragment.instantiate(Fragment.java:672)
       at androidx.fragment.app.FragmentContainer.instantiate(FragmentContainer.java:57)
       at androidx.fragment.app.FragmentManager$3.instantiate(FragmentManager.java:525)
       at androidx.fragment.app.FragmentState.instantiate(FragmentState.java:84)
       at androidx.fragment.app.FragmentStateManager.<init>(FragmentStateManager.java:91)
       at androidx.fragment.app.FragmentManager.restoreSaveStateInternal(FragmentManager.java:2562)
       at androidx.fragment.app.Fragment.restoreChildFragmentState(Fragment.java:1988)
       at androidx.fragment.app.Fragment.onCreate(Fragment.java:1967)
       at androidx.fragment.app.Fragment.performCreate(Fragment.java:3094)
       at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:504)
       at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:268)
       at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:114)
       at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1455)
       at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3034)
       at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2941)
       at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:252)
       at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:219)
       at pro.labster.eboox.app.presentation.main.MainActivity.onCreate(MainActivity.kt:87)
       at android.app.Activity.performCreate(Activity.java:7893)
       at android.app.Activity.performCreate(Activity.java:7880)
       at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1306)
       at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3315)
       at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3489)
       at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:83)
       at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:135)
       at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:95)
       at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2073)
       at android.os.Handler.dispatchMessage(Handler.java:107)
       at android.os.Looper.loop(Looper.java:225)
       at android.app.ActivityThread.main(ActivityThread.java:7563)
       at java.lang.reflect.Method.invoke(Method.java)
       at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:539)
       at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:994)

Expected behavior

App doesn't crash

How to reproduce?

Rotate the screen

Environment

  • Readium version:

Development environment

  • OS: macOS 13.6
  • IDE: IDEA 2023.2.2

Testing device

Any device actually

  • Android version: 13
  • Model: Pixel 6
  • Is it an emulator? No

Additional context

  • Are you willing to fix the problem and contribute a pull request? No
@qnga
Copy link
Contributor

qnga commented Sep 26, 2023

Make sure you use the EpubNavigatorFragment in the right way. Specifically, you must register the fragment factory as we do there in the test-app.

@RankoR
Copy link
Author

RankoR commented Sep 26, 2023

@qnga I'm doing almost the same, but after onCreate (because book is being loaded with viewModel and is not ready at the moment of fragment creation). In my case code looks like this:

childFragmentManager.fragmentFactory = readerData.navigatorFactory.createFragmentFactory(
            initialLocator = readerData.initialLocation,
            listener = this,
            initialPreferences = readerData.preferencesManager.preferences.value,
            configuration = EpubNavigatorFragment.Configuration {
                decorationTemplates[DecorationStyleAnnotationMark::class] = annotationMarkTemplate()
                decorationTemplates[DecorationStylePageNumber::class] = pageNumberTemplate()
            }
        )

        if (!isNavigatorInitialized) {
            isNavigatorInitialized = true

            if (!wasInstanceRestored) {
                childFragmentManager.commitNow {
                    add(
                        R.id.fragmentContainer,
                        EpubNavigatorFragment::class.java,
                        Bundle(),
                        EPUB_NAVIGATOR_TAG
                    )
                }
            }

            navigator = childFragmentManager.findFragmentByTag(EPUB_NAVIGATOR_TAG) as EpubNavigatorFragment
            setupNavigator()
        }

As I understand, it happens because after re-creation it tries to restore fragment, but there is no factory set yet.

Unfortunately, there is no easy way to make initial data available on such early stage in my specific case. Could you please suggest any workarounds?

@qnga
Copy link
Contributor

qnga commented Sep 27, 2023

This is tricky. I guess you could solve it by making your fragment hierarchy a bit more complex. Embed the EpubFragmentNavigator in a Fragment that you create only when all data is available in a parent fragment's ViewModel. This parent fragment would display a loading view while it loads data.

@mickael-menu mickael-menu added the wontfix This will not be worked on label Sep 27, 2023
@mickael-menu
Copy link
Member

I'm closing this issue as it works as expected in the toolkit, but feel free to continue the discussion.

I would also recommend @qnga's suggestion for this.

@radusalagean
Copy link

@mickael-menu
I started encountering this crash too when I updated androidx.fragment:fragment-ktx from v1.5.7 to v1.6.0 back in July. I identified that the crash is triggered by this update and I reverted the version as a hotfix. I didn't investigate futher back then - because I was focusing on implementing new features - , and only left a comment in my code as a warning to myself, to avoid updating to v1.6.0.

Now, I updated Android Navigation from 2.5.3 to 2.6.0. That change caused the same crash to happen again. Now I made another hotfix, reverting the Android Navigation version to 2.5.3.

Also, I started searching github and found this issue which is currently marked as closed. I dit a git checkout to the v2.3.0 tag of the Readium Toolkit, and with zero changes, I tried to reproduce the problem.

Reproduction steps:

  • Open an EPUB ebook
  • Put the app in background
  • Run adb shell am kill org.readium.r2reader in your terminal - this will simulate process death
  • Open the app again from the recent activities screen
  • Look in logcat and search for FATAL
  • You will see a crash:
java.lang.RuntimeException: Unable to start activity ComponentInfo{org.readium.r2reader/org.readium.r2.testapp.reader.ReaderActivity}: java.lang.ClassCastException: org.readium.r2.testapp.reader.DummyReaderInitData cannot be cast to org.readium.r2.testapp.reader.EpubReaderInitData
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3782)
                                                                                                    	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922)
                                                                                                    	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139)
                                                                                                    	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96)
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443)
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:106)
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205)
                                                                                                    	at android.os.Looper.loop(Looper.java:294)
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8177)
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method)
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552)
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971)
                                                                                                    Caused by: java.lang.ClassCastException: org.readium.r2.testapp.reader.DummyReaderInitData cannot be cast to org.readium.r2.testapp.reader.EpubReaderInitData
                                                                                                    	at org.readium.r2.testapp.reader.EpubReaderFragment.onCreate(EpubReaderFragment.kt:52)
                                                                                                    	at androidx.fragment.app.Fragment.performCreate(Fragment.java:3090)
                                                                                                    	at androidx.fragment.app.FragmentStateManager.create(FragmentStateManager.java:475)
                                                                                                    	at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:257)
                                                                                                    	at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:113)
                                                                                                    	at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1433)
                                                                                                    	at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:2977)
                                                                                                    	at androidx.fragment.app.FragmentManager.dispatchCreate(FragmentManager.java:2884)
                                                                                                    	at androidx.fragment.app.FragmentController.dispatchCreate(FragmentController.java:252)
                                                                                                    	at androidx.fragment.app.FragmentActivity.onCreate(FragmentActivity.java:220)
                                                                                                    	at org.readium.r2.testapp.reader.ReaderActivity.onCreate(ReaderActivity.kt:61)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:8595)
                                                                                                    	at android.app.Activity.performCreate(Activity.java:8573)
                                                                                                    	at android.app.Instrumentation.callActivityOnCreate(Instrumentation.java:1456)
                                                                                                    	at android.app.ActivityThread.performLaunchActivity(ActivityThread.java:3764)
                                                                                                    	at android.app.ActivityThread.handleLaunchActivity(ActivityThread.java:3922) 
                                                                                                    	at android.app.servertransaction.LaunchActivityItem.execute(LaunchActivityItem.java:103) 
                                                                                                    	at android.app.servertransaction.TransactionExecutor.executeCallbacks(TransactionExecutor.java:139) 
                                                                                                    	at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:96) 
                                                                                                    	at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2443) 
                                                                                                    	at android.os.Handler.dispatchMessage(Handler.java:106) 
                                                                                                    	at android.os.Looper.loopOnce(Looper.java:205) 
                                                                                                    	at android.os.Looper.loop(Looper.java:294) 
                                                                                                    	at android.app.ActivityThread.main(ActivityThread.java:8177) 
                                                                                                    	at java.lang.reflect.Method.invoke(Native Method) 
                                                                                                    	at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:552) 
                                                                                                    	at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:971) 

Yes, this is not the same crash as reported above, but this is the first issue I see that needs to be addressed by the Readium team - related to the issue reported by @RankoR - the sample app crashes when trying to restore from process death, if an EPUB publication was opened when the system decided to kill the process.

The latest toolkit release is from December 28, 2022, so it's almost a year old. I'd love to see the sample app updated, to use the latest dependencies and the crash from above fixed. I'm trying to use the sample app as a guidance, but it's impossible to do so when I update the androidx dependencies in our app to the latest versions, and suddenly crashes start appearing in the Readium toolkit internal classes.

Eagerly waiting for your input, as now I have 2 android dependencies that I cannot update because of this crash. Thank you!

Video demo of the crash

@mickael-menu
Copy link
Member

@radusalagean It's something to fix in your application. Here's a diff that fixes the problem in the test app:

diff --git a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderActivity.kt b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderActivity.kt
index cc8867dc7..e99427446 100644
--- a/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderActivity.kt
+++ b/test-app/src/main/java/org/readium/r2/testapp/reader/ReaderActivity.kt
@@ -49,6 +49,8 @@ open class ReaderActivity : AppCompatActivity() {
     private lateinit var readerFragment: BaseReaderFragment
 
     override fun onCreate(savedInstanceState: Bundle?) {
+        super.onCreate(savedInstanceState)
+
         /*
          * We provide dummy publications if the [ReaderActivity] is restored after the app process
          * was killed because the [ReaderRepository] is empty.
@@ -56,10 +58,9 @@ open class ReaderActivity : AppCompatActivity() {
          */
         if (model.publication.readingOrder.isEmpty()) {
             finish()
+            return
         }
 
-        super.onCreate(savedInstanceState)
-
         val binding = ActivityReaderBinding.inflate(layoutInflater)
         setContentView(binding.root)

I won't fix it in main because it's not part of the toolkit and we're already working on the next v3.

@radusalagean
Copy link

@mickael-menu That diff doesn't solve the crash (video demo). Passing the savedInstanceState to the super.onCreate(...) call makes Android attempt to restore the saved state of the activity, including the EpubReaderFragment where the crash occurs.
However, this works and doesn't crash the app:

override fun onCreate(savedInstanceState: Bundle?) {
        /*
         * We provide dummy publications if the [ReaderActivity] is restored after the app process
         * was killed because the [ReaderRepository] is empty.
         * In that case, finish the activity as soon as possible and go back to the previous one.
         */
        if (model.publication.readingOrder.isEmpty()) {
+           super.onCreate(null)
            finish()
+           return
        }

        super.onCreate(savedInstanceState)

        val binding = ActivityReaderBinding.inflate(layoutInflater)
        setContentView(binding.root)

Related to the EpubNavigatorFragment instantiation crash that I described in my previous comment, I think I found the reason why it started appearing in my app after updating androidx.fragment:fragment-ktx to 1.6.0. In their release notes for 1.6.0, they mention this breaking change:

The saved state of Fragments has been split entirely between private library state (custom Parcelable classes) and state provided by the developer, which is now always stored in a Bundle that allows determining exactly where a fragment’s state is originating.

In my app, I have a single Activity and multiple fragments. So in EpubReaderFragment.kt, onCreate looks like this:

override fun onCreate(savedInstanceState: Bundle?) {
    val readerInitData = viewModel.readerInitData as? EpubReaderInitData
    if (readerInitData == null) {
        navController?.popBackStack()
    } else {
        childFragmentManager.fragmentFactory =
            readerInitData.navigatorFactory.createFragmentFactory(
                initialLocator = readerInitData.initialLocation,
                listener = this,
                initialPreferences = readerInitData.preferencesManager.preferences.value,
                configuration = EpubNavigatorFragment.Configuration {
                    // Config stuff here
                }
            )
    }
    super.onCreate(if (readerInitData == null) null else savedInstanceState)
}

Emphasis on the last line: super.onCreate(if (readerInitData == null) null else savedInstanceState).
Before androidx.fragment:fragment-ktx:1.6.0, if I passed null to super.onCreate(...), then Android didn't attempt to restore nested fragments - in this case EpubNavigatorFragment - so when the app was restored, the user was sent back to the previous fragment and no crashes occurred.

After the library update, it tries to restore the fragment, hence the crash.

I'm stuck on this, as I don't know any way to "discard" the fragment before the OS attempts to restore it. I think Android never meant for the process restoration mechanism to bypass the restoration of fragments. As far as I know, they always pushed devs to properly restore the state the user was in before the OS killed the app.

For now I will keep androidx.fragment:fragment-ktx to 1.5.7, but this will become more difficult to do as time passes, because other libraries will start to depend on 1.6.0+ like navigation, as I described previously - which will keep me stuck on increasingly old system library releases. I kindly ask you to consider the Single Activity Multi Fragment architecture and provide advice on how we can gracefully handle this essential app restoration case when using androidx.fragment:fragment-ktx 1.6.0+.

@qnga 's suggestion does not apply here, because the system attempts to restore the fragment hierarchy, and the EpubNavigatorFragment factory expects us to pass publication data before it is ready. I think this is an architectural oversight in the toolkit.

I understand that v3 is in development, but until that is stable and released, we need the main branch to be maintained more often, as system libraries update and have new breaking changes.

I hope I didn't come off as rude or demanding in any way, that is not my intention, I'm just trying to explain that as far as I'm concerned, this is not something that I can fix now on my side with my current Single Activity Multi Fragment architecture.

Screenshot 2023-11-02 at 19 57 59
(source)

If you agree, please reopen this issue and acknowledge it so my client has visibility of this. Thank you!

@mickael-menu
Copy link
Member

Any reason you are sticking with androidx.fragment:fragment-ktx:1.5.7 even with the fix that seems to work for you?

I agree this should be addressed in the toolkit though, I'm reopening this issue.

However, I'm still unsure about the proper way to fix it using an API change. What would the ideal API look like to you?

@qnga 's #395 (comment) does not apply here, because the system attempts to restore the fragment hierarchy, and the EpubNavigatorFragment factory expects us to pass publication data before it is ready.

Let's say you provide a suspending closure to return a Publication asynchronously to the EpubNavigatorFragment. It will render the publication resources in additional child fragments. Now if the system kills then restores the app, Android will try to restore the child fragments of the EpubNavigatorFragment before the Publication is loaded. Aren't we back to square one if we don't prevent the restoration of some fragments in the hierarchy while the Publication is being opened again?

@mickael-menu mickael-menu reopened this Nov 2, 2023
@mickael-menu mickael-menu added bug Something isn't working and removed wontfix This will not be worked on labels Nov 2, 2023
@radusalagean
Copy link

Any reason you are sticking with androidx.fragment:fragment-ktx:1.5.7 even with the fix that seems to work for you?

That workaround I showed in my previous comment works in the context of the Readium Sample app, which uses a dedicated Reader Activity. Calling super.onCreate(null) in that activity when the publication is not ready, prevents restoring the child fragments - and thus prevents the crash from happening. It is not a fix for the problem, it is a workaround, and it works if you have the reader extracted in a dedicated activity. I cannot do that in my app, as I have a single activity multi fragment architecture. Extracting the reader in a dedicated activity is not feasible for me.

Let's say you provide a suspending closure to return a Publication asynchronously to the EpubNavigatorFragment. It will render the publication resources in additional child fragments. Now if the system kills then restores the app, Android will try to restore the child fragments of the EpubNavigatorFragment before the Publication is loaded. Aren't we back to square one if we don't prevent the restoration of some fragments in the hierarchy while the Publication is being opened again?

I understand your point - and I agree, preventing restoration of fragments in the hierarchy is preferred in this case, as the alternative would probably be more prone to bugs and involve signigicant changes in EpubNavigatorFragment and other places.
EpubNavigatorFragment expects the publication to be available immediately in the constructor. This is the problem.
Conceptually, Fragments were indended to receive small amounts of "seed" data from the outside when being instantiated, like primitives or small serializable data structures that can then be put in the savedInstanceState - only the bare minimum that's needed for that Fragment to autonomously trigger loading the full data on its own.

However, I'm still unsure about the proper way to fix it using an API change. What would the ideal API look like to you?

If you can find a way so we can start updating androidx.fragment:fragment-ktx in our apps past 1.6.0, not have it crash when restoring from process death and not have to extract the reader in a dedicated activity just to make the super.onCreate(null) workaround in that activity, that will solve the issue. Properly restoring state of the e-reader after recovering from process death is not required.

If you're asking me about the ideal API, that is something that I cannot answer without spending more hours studying your codebase and implications of my suggestions. That would be an architectural assistance request, which means effectively working on the Readium project, something that I'm not currently in the position to do.

However, the ideal API is something that you and your team can establish by following Android's recommendations when building it, such as not expecting data that cannot be passed synchronously (i.e. the publication) in the constructor of the fragment.

Another idea that requires more work is to modify the EpubNavigatorFragment so that it it can accept a nullable publication in the constructor. Then, when the fragment is being initialized, if the Publication null, simply pop it off the backstack. It's something that I have not tested, just laying some ideas that don't require rearchitecting large segments of the toolkit.

I prepared the repo with the bare minimum changes so you can reproduce the crash. You can find the commit here: radusalagean@8131d65

@mickael-menu
Copy link
Member

I understand your point - and I agree, preventing restoration of fragments in the hierarchy is preferred in this case, as the alternative would probably be more prone to bugs and involve signigicant changes in EpubNavigatorFragment and other places.

Do you know a way to prevent fragment restoration since fragment-ktx:1.6.0?

We're aware of the shortcomings of the current Navigators, which extend beyond this particular issue. That's why we've been prototyping a new version from the ground up, which is a substantial task. Meanwhile, we need a solution that does not require a major overhaul of the current navigators.

Would something like that work for you? It initializes the navigator with a dummy publication, but it's still the responsibility of the app to ditch the navigator fragment after restoring the app before the view is displayed on the screen. It seems to address the issue with the commit you shared above.

https://github.com/readium/kotlin-toolkit/pull/418/files

@radusalagean
Copy link

@mickael-menu I tested your change in my project and can confirm that it works. The EpubNavigatorFragment is ditched with my existing code in the parent EpubReaderFragment:

override fun onCreate(savedInstanceState: Bundle?) {
    val readerInitData = viewModel.readerInitData as? EpubReaderInitData
    if (readerInitData == null) {
        navController?.popBackStack() // This 
    } else {
        ...
    }
    ...
}

Thus, when restoring the app, the lifecycle logs show:

EpubReaderFragment onCreate
EpubNavigatorFragment onCreate
EpubNavigatorFragment onDestroy
EpubReaderFragment onDestroy

So we are fine.

This patch also needs to be applied to the PdfNavigatorFragment and that will fix PDFs as well. Can you please do that and then make a new toolkit release so I can then pull it in my project? I appreciate your help, thank you!

@mickael-menu
Copy link
Member

Sure, thank you for testing the fix and bringing this issue to our attention.

@mickael-menu
Copy link
Member

@radusalagean Would you mind confirming these changes work for you with PDF too? #418

I changed the strategy because we can't create a PDF fragment without knowing the PDF engine. Now you need to create a dummy fragment factory, e.g.

override fun onCreate(savedInstanceState: Bundle?) {
    val publication = model.publication ?: run {
        childFragmentManager.fragmentFactory = EpubNavigatorFragment.createDummyFactory()
        super.onCreate(savedInstanceState)

        requireActivity().finish()
        // or
        navController?.popBackStack()

        return
    }

    // Create the real navigator factory as usual...
}

@radusalagean
Copy link

@mickael-menu I imported all the changes from the linked pull request in my project. EPUBs still work fine with the new dummy fragment factory approach. When restoring the app if it had a PDF open prior to the OS killing the app, I'm getting androidx.fragment.app.Fragment$InstantiationException: Unable to instantiate fragment org.readium.adapters.pdfium.navigator.PdfiumDocumentFragment: could not find Fragment constructor

@mickael-menu
Copy link
Member

Did you use the dummy PDF fragment factory too? Could you share your PdfReaderFragment?

@radusalagean
Copy link

Yes, I did. I think the problem is that the nested PdfiumDocumentFragment (instantiated here) has a constructor that requires arguments. When Android is trying to restore the entire hierarchy of fragments, it is looking for a default constructor that requires no arguments.
Here is my PdfReaderFragment

@OptIn(ExperimentalReadiumApi::class)
class PDFReaderFragment : BaseReaderFragment(), PdfNavigatorFragment.Listener {

    override lateinit var navigator: PdfNavigatorFragment<PdfiumSettings, PdfiumPreferences>

    override fun onCreate(savedInstanceState: Bundle?) {
        val readerInitData = viewModel.readerInitData as? PdfReaderInitData
        if (readerInitData == null) {
            childFragmentManager.fragmentFactory = PdfNavigatorFragment.createDummyFactory(
                pdfEngineProvider = PdfiumEngineProvider()
            )
            navController?.popBackStack()
        } else {
            childFragmentManager.fragmentFactory = readerInitData.navigatorFactory
                .createFragmentFactory(
                    initialLocator = readerInitData.initialLocation,
                    initialPreferences = readerInitData.preferencesManager.preferences.value,
                    listener = this
                )
        }
        super.onCreate(if (readerInitData == null) null else savedInstanceState)
    }

    override fun onCreateView(
        inflater: LayoutInflater,
        container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        val view = super.onCreateView(inflater, container, savedInstanceState)
        if (savedInstanceState == null) {
            childFragmentManager.commitNow {
                add(
                    R.id.fragment_reader_container,
                    PdfNavigatorFragment::class.java,
                    Bundle(),
                    NAVIGATOR_FRAGMENT_TAG
                )
            }
        }
        @Suppress("Unchecked_cast")
        navigator = childFragmentManager
            .findFragmentByTag(NAVIGATOR_FRAGMENT_TAG) as PdfNavigatorFragment<PdfiumSettings, PdfiumPreferences>
        return view
    }

    override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
        super.onViewCreated(view, savedInstanceState)
        @Suppress("Unchecked_cast")
        (viewModel.settings as UserPreferencesViewModel<PdfiumSettings, PdfiumPreferences>)
            .bind(navigator, viewLifecycleOwner)
    }

    override fun onResourceLoadFailed(link: Link, error: Resource.Exception) {
        Bugsnag.notify(error)
        val message = when (error) {
            is Resource.Exception.OutOfMemory -> "The PDF is too large to be rendered on this device"
            else -> "Failed to render this PDF"
        }
        Toast.makeText(requireActivity(), message, Toast.LENGTH_LONG).show()

        navController?.popBackStack()
    }

    companion object {
        const val NAVIGATOR_FRAGMENT_TAG = "pdf_navigator"
    }
}

@radusalagean
Copy link

Creating a default constructor in PdfiumDocumentFragment with no arguments would solve the issue

@mickael-menu
Copy link
Member

Ha yes, I didn't try from your fork. It should work now.

@radusalagean
Copy link

All good from my side now 🙏🏻

@mickael-menu
Copy link
Member

2.4.0 is released. Take a look at the migration guide as Readium is now distributed through Maven Central.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
bug Something isn't working
Projects
None yet
Development

No branches or pull requests

4 participants