akexorcist / Localization

[Android] In-app language changing library
Apache License 2.0
983 stars 154 forks source link

Compatibility with Dagger-Hilt ViewModel injection #96

Closed LloydBlv closed 3 years ago

LloydBlv commented 3 years ago

Using the library in a project with dagger hilt that is using by viewModels delegation(Or probably other forms of ViewModel initialisation) and in one of its injected dependencies using @ApplicationContext annotated context causes a crash when a language set call is made.

here is the stack trace:

2021-04-05 07:25:04.778 18792-18792/com.akexorcist.localizationapp E/AndroidRuntime: FATAL EXCEPTION: main
    Process: com.akexorcist.localizationapp, PID: 18792
    java.lang.ClassCastException: android.app.ContextImpl cannot be cast to android.app.Application
        at dagger.hilt.android.internal.modules.ApplicationContextModule.provideApplication(ApplicationContextModule.java:45)
        at dagger.hilt.android.internal.modules.ApplicationContextModule_ProvideApplicationFactory.provideApplication(ApplicationContextModule_ProvideApplicationFactory.java:30)
        at com.akexorcist.localizationapp.DaggerMainApplication_HiltComponents_SingletonC$ActivityRetainedCImpl$ActivityCImpl.getHiltInternalFactoryFactory(DaggerMainApplication_HiltComponents_SingletonC.java:156)
        at com.akexorcist.localizationapp.DaggerMainApplication_HiltComponents_SingletonC$ActivityRetainedCImpl$ActivityCImpl$FragmentCImpl.getHiltInternalFactoryFactory(DaggerMainApplication_HiltComponents_SingletonC.java:211)
        at dagger.hilt.android.internal.lifecycle.DefaultViewModelFactories.getFragmentFactory(DefaultViewModelFactories.java:66)
        at com.akexorcist.localizationapp.hilt.Hilt_HiltFragmentWithViewModel.getDefaultViewModelProviderFactory(Hilt_HiltFragmentWithViewModel.java:108)
        at androidx.fragment.app.FragmentViewModelLazyKt$createViewModelLazy$factoryPromise$1.invoke(FragmentViewModelLazy.kt:93)
        at androidx.fragment.app.FragmentViewModelLazyKt$createViewModelLazy$factoryPromise$1.invoke(Unknown Source:0)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:52)
        at androidx.lifecycle.ViewModelLazy.getValue(ViewModelProvider.kt:41)
        at com.akexorcist.localizationapp.hilt.HiltFragmentWithViewModel.getHiltViewModel(Unknown Source:2)
        at com.akexorcist.localizationapp.hilt.HiltFragmentWithViewModel.setupUi(HiltFragmentWithViewModel.kt:31)
        at com.akexorcist.localizationapp.hilt.HiltFragmentWithViewModel.onViewCreated(HiltFragmentWithViewModel.kt:25)
        at androidx.fragment.app.Fragment.performViewCreated(Fragment.java:2974)
        at androidx.fragment.app.FragmentStateManager.createView(FragmentStateManager.java:543)
        at androidx.fragment.app.FragmentStateManager.moveToExpectedState(FragmentStateManager.java:282)
        at androidx.fragment.app.FragmentStore.moveToExpectedState(FragmentStore.java:112)
        at androidx.fragment.app.FragmentManager.moveToState(FragmentManager.java:1636)
        at androidx.fragment.app.FragmentManager.dispatchStateChange(FragmentManager.java:3112)
        at androidx.fragment.app.FragmentManager.dispatchActivityCreated(FragmentManager.java:3056)
        at androidx.fragment.app.FragmentController.dispatchActivityCreated(FragmentController.java:251)
        at androidx.fragment.app.FragmentActivity.onStart(FragmentActivity.java:473)
        at androidx.appcompat.app.AppCompatActivity.onStart(AppCompatActivity.java:210)
        at android.app.Instrumentation.callActivityOnStart(Instrumentation.java:1435)
        at android.app.Activity.performStart(Activity.java:8024)
        at android.app.ActivityThread.handleStartActivity(ActivityThread.java:3457)
        at android.app.servertransaction.TransactionExecutor.performLifecycleSequence(TransactionExecutor.java:221)
        at android.app.servertransaction.TransactionExecutor.cycleToPath(TransactionExecutor.java:201)
        at android.app.servertransaction.TransactionExecutor.executeLifecycleState(TransactionExecutor.java:173)
        at android.app.servertransaction.TransactionExecutor.execute(TransactionExecutor.java:97)
        at android.app.ClientTransactionHandler.executeTransaction(ClientTransactionHandler.java:58)
        at android.app.ActivityThread.handleRelaunchActivityLocally(ActivityThread.java:5410)
        at android.app.ActivityThread.access$3300(ActivityThread.java:237)
        at android.app.ActivityThread$H.handleMessage(ActivityThread.java:2076)
        at android.os.Handler.dispatchMessage(Handler.java:106)
        at android.os.Looper.loop(Looper.java:223)
        at android.app.ActivityThread.main(ActivityThread.java:7660)
        at java.lang.reflect.Method.invoke(Native Method)
        at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:592)
2021-04-05 07:25:04.778 18792-18792/com.akexorcist.localizationapp E/AndroidRuntime:     at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:947)

this is mainly because of the wrapped context which is returned in the overridden getApplicationContext() call inside the application class. The library is returning an instance of LocalizationContextwhich hilt has difficulties casting it to an Application class in the module below:

/** Provides a binding for an Android BinderFragment Context. */
@Module
@InstallIn(SingletonComponent.class)
public final class ApplicationContextModule {
  private final Context applicationContext;

  public ApplicationContextModule(Context applicationContext) {
    this.applicationContext = applicationContext;
  }

  @Provides
  @ApplicationContext
  Context provideContext() {
    return applicationContext;
  }

  @Provides
  Application provideApplication() {
    return (Application) applicationContext.getApplicationContext();
  }
}

Which is picked from Hilt's source codes. The crash happens on line return (Application) applicationContext.getApplicationContext();