diff --git a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java index 18fb423b87a5b..98896b1183310 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/ExtensionFragmentListener.java @@ -37,9 +37,14 @@ public class ExtensionFragmentListener implements Consum private final int mId; private String mOldClass; - private ExtensionFragmentListener(View view, String tag, int id, Extension extension) { + private ExtensionFragmentListener( + FragmentService fragmentService, + View view, + String tag, + int id, + Extension extension) { mTag = tag; - mFragmentHostManager = FragmentHostManager.get(view); + mFragmentHostManager = fragmentService.getFragmentHostManager(view); mExtension = extension; mId = id; mFragmentHostManager.getFragmentManager().beginTransaction() @@ -61,8 +66,13 @@ public class ExtensionFragmentListener implements Consum mExtension.clearItem(true); } - public static void attachExtensonToFragment(View view, String tag, int id, + public static void attachExtensonToFragment( + FragmentService fragmentService, + View view, + String tag, + int id, Extension extension) { - extension.addCallback(new ExtensionFragmentListener(view, tag, id, extension)); + extension.addCallback( + new ExtensionFragmentListener(fragmentService, view, tag, id, extension)); } } diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java index 9c7411bf36499..6a27ee7c0c891 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentHostManager.java @@ -36,7 +36,6 @@ import android.view.View; import androidx.annotation.NonNull; import com.android.settingslib.applications.InterestingConfigChanges; -import com.android.systemui.Dependency; import com.android.systemui.plugins.Plugin; import com.android.systemui.util.leak.LeakDetector; @@ -46,12 +45,17 @@ import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.HashMap; +import dagger.assisted.Assisted; +import dagger.assisted.AssistedFactory; +import dagger.assisted.AssistedInject; + public class FragmentHostManager { private final Handler mHandler = new Handler(Looper.getMainLooper()); private final Context mContext; private final HashMap> mListeners = new HashMap<>(); private final View mRootView; + private final LeakDetector mLeakDetector; private final InterestingConfigChanges mConfigChanges = new InterestingConfigChanges( ActivityInfo.CONFIG_FONT_SCALE | ActivityInfo.CONFIG_LOCALE | ActivityInfo.CONFIG_ASSETS_PATHS); @@ -61,14 +65,24 @@ public class FragmentHostManager { private FragmentController mFragments; private FragmentLifecycleCallbacks mLifecycleCallbacks; - FragmentHostManager(FragmentService manager, View rootView) { + @AssistedInject + FragmentHostManager( + @Assisted View rootView, + FragmentService manager, + LeakDetector leakDetector) { mContext = rootView.getContext(); mManager = manager; mRootView = rootView; + mLeakDetector = leakDetector; mConfigChanges.applyNewConfig(mContext.getResources()); createFragmentHost(null); } + @AssistedFactory + public interface Factory { + FragmentHostManager create(View rootView); + } + private void createFragmentHost(Parcelable savedState) { mFragments = FragmentController.createController(new HostCallbacks()); mFragments.attachHost(null); @@ -86,7 +100,7 @@ public class FragmentHostManager { @Override public void onFragmentDestroyed(FragmentManager fm, Fragment f) { - Dependency.get(LeakDetector.class).trackGarbage(f); + mLeakDetector.trackGarbage(f); } }; mFragments.getFragmentManager().registerFragmentLifecycleCallbacks(mLifecycleCallbacks, @@ -211,19 +225,6 @@ public class FragmentHostManager { } } - public static FragmentHostManager get(View view) { - try { - return Dependency.get(FragmentService.class).getFragmentHostManager(view); - } catch (ClassCastException e) { - // TODO: Some auto handling here? - throw e; - } - } - - public static void removeAndDestroy(View view) { - Dependency.get(FragmentService.class).removeAndDestroy(view); - } - public void reloadFragments() { Trace.beginSection("FrargmentHostManager#reloadFragments"); // Save the old state. diff --git a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java index fe945fb2fa288..d302b13a68b6b 100644 --- a/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java +++ b/packages/SystemUI/src/com/android/systemui/fragments/FragmentService.java @@ -53,6 +53,7 @@ public class FragmentService implements Dumpable { */ private final ArrayMap mInjectionMap = new ArrayMap<>(); private final Handler mHandler = new Handler(); + private final FragmentHostManager.Factory mFragmentHostManagerFactory; private ConfigurationController.ConfigurationListener mConfigurationListener = new ConfigurationController.ConfigurationListener() { @@ -67,8 +68,10 @@ public class FragmentService implements Dumpable { @Inject public FragmentService( FragmentCreator.Factory fragmentCreatorFactory, + FragmentHostManager.Factory fragmentHostManagerFactory, ConfigurationController configurationController, DumpManager dumpManager) { + mFragmentHostManagerFactory = fragmentHostManagerFactory; addFragmentInstantiationProvider(fragmentCreatorFactory.build()); configurationController.addCallback(mConfigurationListener); @@ -152,7 +155,7 @@ public class FragmentService implements Dumpable { public FragmentHostState(View view) { mView = view; - mFragmentHostManager = new FragmentHostManager(FragmentService.this, mView); + mFragmentHostManager = mFragmentHostManagerFactory.create(mView); } public void sendConfigurationChange(Configuration newConfig) { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt index 85b259e54f37f..de02115184b6c 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQSContainerController.kt @@ -31,6 +31,7 @@ import com.android.systemui.R import com.android.systemui.dagger.qualifiers.Main import com.android.systemui.flags.FeatureFlags import com.android.systemui.flags.Flags +import com.android.systemui.fragments.FragmentService import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.plugins.qs.QS import com.android.systemui.plugins.qs.QSContainerController @@ -54,6 +55,7 @@ class NotificationsQSContainerController @Inject constructor( private val largeScreenShadeHeaderController: LargeScreenShadeHeaderController, private val shadeExpansionStateManager: ShadeExpansionStateManager, private val featureFlags: FeatureFlags, + private val fragmentService: FragmentService, @Main private val delayableExecutor: DelayableExecutor ) : ViewController(view), QSContainerController { @@ -128,6 +130,7 @@ class NotificationsQSContainerController @Inject constructor( mView.setInsetsChangedListener(delayedInsetSetter) mView.setQSFragmentAttachedListener { qs: QS -> qs.setContainerController(this) } mView.setConfigurationChangedListener { updateResources() } + fragmentService.getFragmentHostManager(mView).addTagListener(QS.TAG, mView) } override fun onViewDetached() { @@ -136,6 +139,7 @@ class NotificationsQSContainerController @Inject constructor( mView.removeOnInsetsChangedListener() mView.removeQSFragmentAttachedListener() mView.setConfigurationChangedListener(null) + fragmentService.getFragmentHostManager(mView).removeTagListener(QS.TAG, mView) } fun updateResources() { diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java index 02316b7965cac..f73dde6320519 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationsQuickSettingsContainer.java @@ -29,7 +29,6 @@ import androidx.constraintlayout.widget.ConstraintLayout; import androidx.constraintlayout.widget.ConstraintSet; import com.android.systemui.R; -import com.android.systemui.fragments.FragmentHostManager; import com.android.systemui.fragments.FragmentHostManager.FragmentListener; import com.android.systemui.plugins.qs.QS; import com.android.systemui.statusbar.notification.AboveShelfObserver; @@ -132,18 +131,6 @@ public class NotificationsQuickSettingsContainer extends ConstraintLayout mQSFragmentAttachedListener = qs -> {}; } - @Override - protected void onAttachedToWindow() { - super.onAttachedToWindow(); - FragmentHostManager.get(this).addTagListener(QS.TAG, this); - } - - @Override - protected void onDetachedFromWindow() { - super.onDetachedFromWindow(); - FragmentHostManager.get(this).removeTagListener(QS.TAG, this); - } - @Override public WindowInsets onApplyWindowInsets(WindowInsets insets) { mInsetsChangedListener.accept(insets); diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java index 5ff755e10a6c1..85399ca22d968 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java @@ -1306,8 +1306,13 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { // Set up the quick settings tile panel final View container = mNotificationShadeWindowView.findViewById(R.id.qs_frame); if (container != null) { - FragmentHostManager fragmentHostManager = FragmentHostManager.get(container); - ExtensionFragmentListener.attachExtensonToFragment(container, QS.TAG, R.id.qs_frame, + FragmentHostManager fragmentHostManager = + mFragmentService.getFragmentHostManager(container); + ExtensionFragmentListener.attachExtensonToFragment( + mFragmentService, + container, + QS.TAG, + R.id.qs_frame, mExtensionController .newExtension(QS.class) .withPlugin(QS.class) @@ -1478,7 +1483,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces { } protected QS createDefaultQSFragment() { - return FragmentHostManager.get(mNotificationShadeWindowView).create(QSFragment.class); + return mFragmentService + .getFragmentHostManager(mNotificationShadeWindowView) + .create(QSFragment.class); } private void setUpPresenter() { diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java index e0d780a5fcd50..7a4e35f159ab7 100644 --- a/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java +++ b/packages/SystemUI/src/com/android/systemui/statusbar/window/StatusBarWindowController.java @@ -48,6 +48,7 @@ import com.android.systemui.animation.DelegateLaunchAnimatorController; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dagger.qualifiers.Main; import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; import com.android.systemui.statusbar.phone.StatusBarContentInsetsProvider; import com.android.systemui.unfold.UnfoldTransitionProgressProvider; import com.android.systemui.unfold.util.JankMonitorTransitionProgressListener; @@ -73,6 +74,7 @@ public class StatusBarWindowController { private boolean mIsAttached; private final ViewGroup mStatusBarWindowView; + private final FragmentService mFragmentService; // The container in which we should run launch animations started from the status bar and // expanding into the opening window. private final ViewGroup mLaunchAnimationContainer; @@ -86,6 +88,7 @@ public class StatusBarWindowController { WindowManager windowManager, IWindowManager iWindowManager, StatusBarContentInsetsProvider contentInsetsProvider, + FragmentService fragmentService, @Main Resources resources, Optional unfoldTransitionProgressProvider) { mContext = context; @@ -93,6 +96,7 @@ public class StatusBarWindowController { mIWindowManager = iWindowManager; mContentInsetsProvider = contentInsetsProvider; mStatusBarWindowView = statusBarWindowView; + mFragmentService = fragmentService; mLaunchAnimationContainer = mStatusBarWindowView.findViewById( R.id.status_bar_launch_animation_container); mLpChanged = new WindowManager.LayoutParams(); @@ -157,7 +161,7 @@ public class StatusBarWindowController { /** Returns a fragment host manager for the status bar window view. */ public FragmentHostManager getFragmentHostManager() { - return FragmentHostManager.get(mStatusBarWindowView); + return mFragmentService.getFragmentHostManager(mStatusBarWindowView); } /** diff --git a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java index 79811c5de42d0..24758908cff2c 100644 --- a/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java +++ b/packages/SystemUI/src/com/android/systemui/tuner/RadioListPreference.java @@ -28,8 +28,9 @@ import androidx.preference.Preference; import androidx.preference.PreferenceScreen; import com.android.settingslib.Utils; +import com.android.systemui.Dependency; import com.android.systemui.R; -import com.android.systemui.fragments.FragmentHostManager; +import com.android.systemui.fragments.FragmentService; import java.util.Objects; @@ -74,7 +75,7 @@ public class RadioListPreference extends CustomListPreference { RadioFragment f = new RadioFragment(); f.setPreference(this); - FragmentHostManager.get(v).getFragmentManager() + Dependency.get(FragmentService.class).getFragmentHostManager(v).getFragmentManager() .beginTransaction() .add(android.R.id.content, f) .commit(); @@ -86,8 +87,10 @@ public class RadioListPreference extends CustomListPreference { Bundle savedInstanceState) { super.onDialogStateRestored(fragment, dialog, savedInstanceState); View view = dialog.findViewById(R.id.content); - RadioFragment radioFragment = (RadioFragment) FragmentHostManager.get(view) - .getFragmentManager().findFragmentById(R.id.content); + RadioFragment radioFragment = (RadioFragment) Dependency.get(FragmentService.class) + .getFragmentHostManager(view) + .getFragmentManager() + .findFragmentById(R.id.content); if (radioFragment != null) { radioFragment.setPreference(this); } diff --git a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt index 77c837b803af5..a2dc1eb606ed8 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/fragments/FragmentServiceTest.kt @@ -14,6 +14,7 @@ import org.junit.Test @SmallTest class FragmentServiceTest : SysuiTestCase() { private val fragmentCreator = TestFragmentCreator() + private val fragmenetHostManagerFactory: FragmentHostManager.Factory = mock() private val fragmentCreatorFactory = FragmentService.FragmentCreator.Factory { fragmentCreator } private lateinit var fragmentService: FragmentService @@ -24,7 +25,13 @@ class FragmentServiceTest : SysuiTestCase() { Looper.prepare() } - fragmentService = FragmentService(fragmentCreatorFactory, mock(), DumpManager()) + fragmentService = + FragmentService( + fragmentCreatorFactory, + fragmenetHostManagerFactory, + mock(), + DumpManager() + ) } @Test diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt index bdafc7df33bca..c915502ad42ea 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationQSContainerControllerTest.kt @@ -13,8 +13,11 @@ import androidx.test.filters.SmallTest import com.android.systemui.R import com.android.systemui.SysuiTestCase import com.android.systemui.flags.FeatureFlags +import com.android.systemui.fragments.FragmentHostManager +import com.android.systemui.fragments.FragmentService import com.android.systemui.navigationbar.NavigationModeController import com.android.systemui.navigationbar.NavigationModeController.ModeChangedListener +import com.android.systemui.plugins.qs.QS import com.android.systemui.recents.OverviewProxyService import com.android.systemui.recents.OverviewProxyService.OverviewProxyListener import com.android.systemui.util.concurrency.FakeExecutor @@ -29,6 +32,7 @@ import org.mockito.Captor import org.mockito.Mock import org.mockito.Mockito import org.mockito.Mockito.RETURNS_DEEP_STUBS +import org.mockito.Mockito.any import org.mockito.Mockito.anyInt import org.mockito.Mockito.doNothing import org.mockito.Mockito.eq @@ -69,6 +73,10 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { private lateinit var shadeExpansionStateManager: ShadeExpansionStateManager @Mock private lateinit var featureFlags: FeatureFlags + @Mock + private lateinit var fragmentService: FragmentService + @Mock + private lateinit var fragmentHostManager: FragmentHostManager @Captor lateinit var navigationModeCaptor: ArgumentCaptor @Captor @@ -77,6 +85,8 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { lateinit var windowInsetsCallbackCaptor: ArgumentCaptor> @Captor lateinit var constraintSetCaptor: ArgumentCaptor + @Captor + lateinit var attachStateListenerCaptor: ArgumentCaptor private lateinit var controller: NotificationsQSContainerController private lateinit var navigationModeCallback: ModeChangedListener @@ -91,8 +101,10 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { mContext.ensureTestableResources() whenever(notificationsQSContainer.context).thenReturn(mContext) whenever(notificationsQSContainer.resources).thenReturn(mContext.resources) + whenever(fragmentService.getFragmentHostManager(any())).thenReturn(fragmentHostManager) fakeSystemClock = FakeSystemClock() delayableExecutor = FakeExecutor(fakeSystemClock) + controller = NotificationsQSContainerController( notificationsQSContainer, navigationModeController, @@ -100,6 +112,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { largeScreenShadeHeaderController, shadeExpansionStateManager, featureFlags, + fragmentService, delayableExecutor ) @@ -114,9 +127,10 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { doNothing().`when`(notificationsQSContainer) .setInsetsChangedListener(windowInsetsCallbackCaptor.capture()) doNothing().`when`(notificationsQSContainer).applyConstraints(constraintSetCaptor.capture()) - + doNothing().`when`(notificationsQSContainer) + .addOnAttachStateChangeListener(attachStateListenerCaptor.capture()) controller.init() - controller.onViewAttached() + attachStateListenerCaptor.value.onViewAttachedToWindow(notificationsQSContainer) navigationModeCallback = navigationModeCaptor.value taskbarVisibilityCallback = taskbarVisibilityCaptor.value @@ -385,6 +399,7 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { largeScreenShadeHeaderController, shadeExpansionStateManager, featureFlags, + fragmentService, delayableExecutor ) controller.updateConstraints() @@ -426,6 +441,17 @@ class NotificationQSContainerControllerTest : SysuiTestCase() { verify(largeScreenShadeHeaderController).startCustomizingAnimation(false, 100L) } + @Test + fun testTagListenerAdded() { + verify(fragmentHostManager).addTagListener(eq(QS.TAG), eq(notificationsQSContainer)) + } + + @Test + fun testTagListenerRemoved() { + attachStateListenerCaptor.value.onViewDetachedFromWindow(notificationsQSContainer) + verify(fragmentHostManager).removeTagListener(eq(QS.TAG), eq(notificationsQSContainer)) + } + private fun disableSplitShade() { setSplitShadeEnabled(false) }