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

WindowCallbackWrapper is removed when action bar is applied in a fragment. #27

Open
vicidroiddev opened this issue Oct 14, 2021 · 0 comments

Comments

@vicidroiddev
Copy link

vicidroiddev commented Oct 14, 2021

I noticed this issue when testing an app that uses fragments to set the toolbar.

MyFragment.kt

override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
  super.onViewCreated(view, savedInstanceState)
  // This causes the activity's window callback to be reset to `mAppCompatWindowCallback`
  (requireActivity() as AppCompatActivity).setSupportActionBar(toolbar)
}

The root cause can be seen in AppCompatDelegateImpl.

AppCompatDelegateImpl.java

    @Override
   public void setSupportActionBar(Toolbar toolbar) {
      // Below the windowCallback is reset based on `mAppCompatWindowCallback` or some wrapped variant. Curtain's wrapper will be overridden.
       if (toolbar != null) {
           final ToolbarActionBar tbab = new ToolbarActionBar(toolbar, getTitle(),
                   mAppCompatWindowCallback);
           mActionBar = tbab;
           mWindow.setCallback(tbab.getWrappedWindowCallback());
       } else {
           mActionBar = null;
           // Re-set the original window callback since we may have already set a Toolbar wrapper
           mWindow.setCallback(mAppCompatWindowCallback);
       }

       invalidateOptionsMenu();
   }

Longer explanation:

Setting a toolbar in Activitity.onCreate() is not an issue since mWindow.setCallback(...) above runs well before the ActivityThread goes through the process of adding a new view to the global window manager.

Curtains relies on modifications of mViews in the window manager via the spy delegator list. If setSupportActionBar runs after the view is added to the window manager, Curtains can't attach its window callback. Curtains would have to wait until addView is called again (which is not the case for fragments).

Possible workarounds

  1. Modify mAppCompatWindowCallback to be the Curtains callback wrapper, such that any subsequent setSupportActionBar() call retains the Curtains window callback.

  2. In WindowCallbackWrapper.Companion add something to check if the callback is the Curtain's variant (or just invoke Window.listeners which does this refresh itself). Now the question is, when to best invoke this - perhaps from a fragment lifecycle listener via onFragmentStarted?

   fun Window.reapplyWindowCallbackWrapper(): Boolean {
     synchronized(listenersLock) {
       if (this.callback is WindowCallbackWrapper) return false
       val wrapperToApply = callbackCache[this]?.get() ?: WindowCallbackWrapper(this.callback)
       this.callback = wrapperToApply
       callbackCache[this] = WeakReference(wrapperToApply)
       return true
     }
   }
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant