B3nedikt / restring

Restring is a android library to replace string resources dynamically
Apache License 2.0
319 stars 31 forks source link

Runtime issues when using setDefaultNightMode #33

Closed edgar-zigis closed 4 years ago

edgar-zigis commented 4 years ago

Hi again, I am reporting you another issue again. So everything did work fine until we have updated the androidx.appcompat into the version 1.2.0

We have an option to set day/night mode inside the app. We change it by calling for example

setDefaultNightMode(MODE_NIGHT_NO) or 
setDefaultNightMode(MODE_NIGHT_YES) or 
setDefaultNightMode(MODE_NIGHT_FOLLOW_SYSTEM)

and after calling this code the app hangs and switches off.

When creating activity we call setDefaultNightMode in the activity onCreate() and also our Restring related activity code looks like this:

override fun attachBaseContext(newBase: Context?) {
        if (newBase != null) {
            val config = Configuration()
            config.setLocale(Locale(ApplicationLanguageManager.getAppLocale(newBase)))
            val restringContext = ViewPumpContextWrapper.wrap(Restring.wrapContext(
                newBase.createConfigurationContext(config)
            ))
            super.attachBaseContext(restringContext)
        } else {
            super.attachBaseContext(newBase)
        }
    }

    override fun getResources(): Resources {
        return Restring.wrapContext(baseContext).resources
    }

    override fun applyOverrideConfiguration(overrideConfiguration: Configuration?) {
        if (overrideConfiguration != null) {
            val uiMode = overrideConfiguration.uiMode
            overrideConfiguration.setTo(baseContext.resources.configuration)
            overrideConfiguration.uiMode = uiMode
        }
        super.applyOverrideConfiguration(overrideConfiguration)
    }

Console report:

java.lang.StackOverflowError: stack size 8MB
        at android.content.ContextWrapper.<init>(ContextWrapper.java:61)
        at android.view.ContextThemeWrapper.<init>(ContextThemeWrapper.java:60)
        at dev.b3nedikt.restring.RestringContextWrapper.<init>(RestringContextWrapper.kt:13)
        at dev.b3nedikt.restring.RestringContextWrapper.<init>(RestringContextWrapper.kt:10)
        at dev.b3nedikt.restring.RestringContextWrapper$Companion.wrap(RestringContextWrapper.kt:25)
        at dev.b3nedikt.restring.Restring.wrapContext(Restring.kt:66)
        at com.paysera.core.base.BaseActivity.getResources(BaseActivity.kt:174)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2500)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2535)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)

it looks like the following activity code is going into the loop. Everything did work fine with AppCompat 1.1.0

override fun getResources(): Resources {
    return Restring.wrapContext(baseContext).resources
}
B3nedikt commented 4 years ago

Two issues with you code.

1. This part here loops in your log:

 at androidx.appcompat.app.AppCompatDelegateImpl.updateForNightMode(AppCompatDelegateImpl.java:2500)
        at androidx.appcompat.app.AppCompatDelegateImpl.applyDayNight(AppCompatDelegateImpl.java:2374)
        at androidx.appcompat.app.AppCompatDelegateImpl.onConfigurationChanged(AppCompatDelegateImpl.java:652)
        at androidx.appcompat.app.AppCompatActivity.onConfigurationChanged(AppCompatActivity.java:199)
        at androidx.appcompat.app.AppCompatDelegateImpl.updateResourcesConfigurationForNightMode(AppCompatDelegateImpl.java:2592)

Why do you assume the activity goes into a loop at getResources(), does removing this line stop the loop for you?

  1. You do lots of stuff in applyOverrideConfiguration() that should not be necessary. Also restring has simpler ways to control the app language. Just look in the sample app of this repository. You need to provide a "LocaleProvider" to restring at startup, the demo app shows how to do this using my lib AppLocale, but it should be easy to implement with your "ApplicationLanguageManager" as well. Your way above should still work though, just more complicated than it needs to be ;)

An interesting issue though, but I can´t reproduce it. I created a sample in this branch how to implement a working night mode switch with: https://github.com/B3nedikt/restring/tree/night_mode_toogle_test

It also fixes a bug with the new restring only string and appcompat 1.2 which I will release as a bug fix tomorrow. Please fork this branch and try to recreate your error on it, so I can see what is going wrong :)

edgar-zigis commented 4 years ago

Hey, thanks for the quick response.

    • I meant that it was the entry point for the infinite loop as that part of BaseActivity did match the getResources line. The issue is probably somewhere internally.
    • You might be right as that part of the code is there for quite a while. Will try to make a fork and report quickly the results.
edgar-zigis commented 4 years ago

@B3nedikt I don't see any commits in that branch, maybe you did forget to push some?

B3nedikt commented 4 years ago

Ah, I forgot to push, just did so now ;)

edgar-zigis commented 4 years ago

This does work fine in the example, but in my application which basically has now the same BaseActivity structure and same version of the libs it ain't cutting. I will try to take a deeper look on it and report what have I found.

However, removing the following code fragment helps (and also kills translations):

override fun getResources(): Resources {
    return Restring.wrapContext(baseContext).resources
}
edgar-zigis commented 4 years ago

So I got the clue now. The issue is within ViewPump library which you don't maintain.. everything fixes when I remove ViewPump wraps. Issue probably can be closed now.

edgar-zigis commented 4 years ago

I managed to get my app to work with webviews, but now the issue is... that night mode does not work with 4.0.5. It works with 4.0.4 though..

B3nedikt commented 4 years ago

Just rebased my test branch and it still works for me with 4.0.6: Night Mode Test Still the same error message as before?

I have no clue which change in 4.0.5 could cause any issues with the night mode. However I made a PR to ViewPump, which makes updating the context easier, this will fix some issues around resources not getting found. This could potentially also fix some issues with night mode themes, but of course not you're stack overflow from above. I am not sure if ViewPump is still maintained though, if not I may need to fork ;)

edgar-zigis commented 4 years ago

@B3nedikt the change was updating appcompat in your library. Somehow updating appcompat to 1.2.0 breaks manual change of day-night mode for us (this is why 4.0.4 did work and 4.0.5 did not). No idea why though, but recreating activity just fails. In our app we use appcompat 1.1.0, we even upgraded it to 1.2.0 and the issue still persists, there is a recursion happening when changing the theme. Day-night mode works fine though after app is relaunched.