Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 71b45a05 authored by Winson Chung's avatar Winson Chung
Browse files

Move handheld-specific controllers out of base module

- Provide a way for overridable impls of *required* base components
  so we don't have to duplicate across various projects, and also use
  it to have the specialized modules provide impls dyanmically
- Use the above to replace nested optional usage for FreeformTaskListner
- Also remove unused/duplicated split controller providers from TV module

Bug: 205019015
Test: mp SystemUIGoogle
Test: mp CarSystemUI
Test: mp ArcSystemUI
Change-Id: If56b18e5814d1fa3d66f5ff04fa84ac6dd54f453
parent 0f90c6e5
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -74,7 +74,7 @@ public class ShellInitImpl {
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            FullscreenTaskListener fullscreenTaskListener,
            Optional<FullscreenUnfoldController> fullscreenUnfoldTransitionController,
            Optional<Optional<FreeformTaskListener>> freeformTaskListenerOptional,
            Optional<FreeformTaskListener> freeformTaskListenerOptional,
            Optional<RecentTasksController> recentTasks,
            Transitions transitions,
            StartingWindowController startingWindow,
@@ -90,7 +90,7 @@ public class ShellInitImpl {
        mFullscreenTaskListener = fullscreenTaskListener;
        mPipTouchHandlerOptional = pipTouchHandlerOptional;
        mFullscreenUnfoldController = fullscreenUnfoldTransitionController;
        mFreeformTaskListenerOptional = freeformTaskListenerOptional.flatMap(f -> f);
        mFreeformTaskListenerOptional = freeformTaskListenerOptional;
        mRecentTasks = recentTasks;
        mTransitions = transitions;
        mMainExecutor = mainExecutor;
+119 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2021 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.android.wm.shell.dagger;

import java.lang.annotation.Documented;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import javax.inject.Qualifier;

/**
 * This is a qualifier that Shell uses to workaround an issue with providing nullable optionals
 * which are by default unbound.
 *
 * For example, ideally we would have this scenario:
 * BaseModule:
 *   @BindsOptionalOf
 *   abstract Optional<Interface> optionalInterface();
 *
 * SpecializedModule:
 *   @Provides
 *   static Interface providesInterface() {
 *       return new InterfaceImpl();
 *   }
 *
 * However, if the interface is supposed to be provided dynamically, then Dagger is not able to bind
 * the optional interface to a null instance, and @BindsOptionalOf does not support @Nullable
 * instances of the interface provided by the specialized module.
 *
 * For example, this does not work:
 * BaseModule:
 *   @BindsOptionalOf
 *   abstract Optional<Interface> optionalInterface();
 *
 * SpecializedModule:
 *   @Provides
 *   static Interface providesInterface() {
 *       if (systemSupportsInterfaceFeature) {
 *           return new InterfaceImpl();
 *       } else {
 *           return null;
 *       }
 *   }
 *
 * To workaround this, we can instead upstream the check (assuming it can be upstreamed into the
 * base module), and then always provide a non-null instance in the specialized module.
 *
 * For example:
 * BaseModule:
 *   @BindsOptionalOf
 *   @DynamicOverride
 *   abstract Interface dynamicInterface();
 *
 *   @Provides
 *   static Optional<Interface> providesOptionalInterface(
 *           @DynamicOverride Optional<Interface> interface) {
 *       if (systemSupportsInterfaceFeature) {
 *           return interface;
 *       }
 *       return Optional.empty();
 *   }
 *
 * SpecializedModule:
 *   @Provides
 *   @DynamicOverride
 *   static Interface providesInterface() {
 *       return new InterfaceImpl();
 *   }
 *
 * This is also useful in cases where there needs to be a default implementation in the base module
 * which is also overridable in the specialized module.  This isn't generally recommended, but
 * due to the nature of Shell modules being referenced from a number of various projects, this
 * can be useful for *required* components that
 * 1) clearly identifies which are intended for overriding in the base module, and
 * 2) allows us to declare a default implementation in the base module, without having to force
 *    every SysUI impl to explicitly provide it (if a large number of them share the default impl)
 *
 * For example, this uses the same setup as above, but the interface provided (if bound) is used
 * otherwise the default is created:
 *   @BindsOptionalOf
 *   @DynamicOverride
 *   abstract Interface dynamicInterface();
 *
 *   @Provides
 *   static Optional<Interface> providesOptionalInterface(
 *           @DynamicOverride Optional<Interface> overrideInterfaceImpl) {
 *       if (overrideInterfaceImpl.isPresent()) {
 *           return overrideInterfaceImpl.get();
 *       }
 *       return new DefaultImpl();
 *   }
 *
 * SpecializedModule:
 *   @Provides
 *   @DynamicOverride
 *   static Interface providesInterface() {
 *       return new SuperSpecialImpl();
 *   }
 */
@Documented
@Inherited
@Qualifier
@Retention(RetentionPolicy.RUNTIME)
public @interface DynamicOverride {}
 No newline at end of file
+1 −40
Original line number Diff line number Diff line
@@ -16,25 +16,16 @@

package com.android.wm.shell.dagger;

import android.animation.AnimationHandler;
import android.content.Context;
import android.view.IWindowManager;

import com.android.wm.shell.ShellTaskOrganizer;
import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.DisplayImeController;
import com.android.wm.shell.common.DisplayInsetsController;
import com.android.wm.shell.common.ShellExecutor;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.common.SystemWindows;
import com.android.wm.shell.common.TaskStackListenerImpl;
import com.android.wm.shell.common.TransactionPool;
import com.android.wm.shell.common.annotations.ChoreographerSfVsync;
import com.android.wm.shell.common.annotations.ShellMainThread;
import com.android.wm.shell.legacysplitscreen.LegacySplitScreenController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.tv.TvStartingWindowTypeAlgorithm;
import com.android.wm.shell.transition.Transitions;

import dagger.Module;
import dagger.Provides;
@@ -50,43 +41,13 @@ import dagger.Provides;
@Module(includes = {TvPipModule.class})
public class TvWMShellModule {

    //
    // Internal common - Components used internally by multiple shell features
    //

    @WMSingleton
    @Provides
    static DisplayImeController provideDisplayImeController(IWindowManager wmService,
            DisplayController displayController, DisplayInsetsController displayInsetsController,
            @ShellMainThread ShellExecutor mainExecutor, TransactionPool transactionPool) {
        return new DisplayImeController(wmService, displayController, displayInsetsController,
                mainExecutor, transactionPool);
    }

    //
    // Split/multiwindow
    //

    @WMSingleton
    @Provides
    static LegacySplitScreenController provideSplitScreen(Context context,
            DisplayController displayController, SystemWindows systemWindows,
            DisplayImeController displayImeController, TransactionPool transactionPool,
            ShellTaskOrganizer shellTaskOrganizer, SyncTransactionQueue syncQueue,
            TaskStackListenerImpl taskStackListener, Transitions transitions,
            @ShellMainThread ShellExecutor mainExecutor,
            @ChoreographerSfVsync AnimationHandler sfVsyncAnimationHandler) {
        return new LegacySplitScreenController(context, displayController, systemWindows,
                displayImeController, transactionPool, shellTaskOrganizer, syncQueue,
                taskStackListener, transitions, mainExecutor, sfVsyncAnimationHandler);
    }

    //
    // Starting Windows (Splash Screen)
    //

    @WMSingleton
    @Provides
    @DynamicOverride
    static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm() {
        return new TvStartingWindowTypeAlgorithm();
    };
+113 −109
Original line number Diff line number Diff line
@@ -16,16 +16,16 @@

package com.android.wm.shell.dagger;

import static com.android.wm.shell.onehanded.OneHandedController.SUPPORT_ONE_HANDED_MODE;

import android.app.ActivityTaskManager;
import android.content.Context;
import android.content.pm.LauncherApps;
import android.content.pm.PackageManager;
import android.os.Handler;
import android.os.SystemProperties;
import android.view.IWindowManager;
import android.view.WindowManager;

import com.android.internal.logging.UiEventLogger;
import com.android.internal.statusbar.IStatusBarService;
import com.android.launcher3.icons.IconProvider;
import com.android.wm.shell.RootDisplayAreaOrganizer;
import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
@@ -77,23 +77,19 @@ import com.android.wm.shell.recents.RecentTasksController;
import com.android.wm.shell.sizecompatui.SizeCompatUIController;
import com.android.wm.shell.splitscreen.SplitScreen;
import com.android.wm.shell.splitscreen.SplitScreenController;
import com.android.wm.shell.splitscreen.StageTaskUnfoldController;
import com.android.wm.shell.startingsurface.StartingSurface;
import com.android.wm.shell.startingsurface.StartingWindowController;
import com.android.wm.shell.startingsurface.StartingWindowTypeAlgorithm;
import com.android.wm.shell.startingsurface.phone.PhoneStartingWindowTypeAlgorithm;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelper;
import com.android.wm.shell.tasksurfacehelper.TaskSurfaceHelperController;
import com.android.wm.shell.transition.ShellTransitions;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.unfold.ShellUnfoldProgressProvider;
import com.android.wm.shell.unfold.UnfoldBackgroundController;

import java.util.Optional;

import javax.inject.Provider;

import dagger.BindsOptionalOf;
import dagger.Lazy;
import dagger.Module;
import dagger.Provides;

@@ -128,6 +124,28 @@ public abstract class WMShellBaseModule {
        return new DisplayInsetsController(wmService, displayController, mainExecutor);
    }

    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract DisplayImeController optionalDisplayImeController();

    @WMSingleton
    @Provides
    static DisplayImeController provideDisplayImeController(
            @DynamicOverride Optional<DisplayImeController> overrideDisplayImeController,
            IWindowManager wmService,
            DisplayController displayController,
            DisplayInsetsController displayInsetsController,
            @ShellMainThread ShellExecutor mainExecutor,
            TransactionPool transactionPool
    ) {
        if (overrideDisplayImeController.isPresent()) {
            return overrideDisplayImeController.get();
        }
        return new DisplayImeController(wmService, displayController, displayInsetsController,
                mainExecutor, transactionPool);
    }

    @WMSingleton
    @Provides
    static DisplayLayout provideDisplayLayout() {
@@ -202,7 +220,7 @@ public abstract class WMShellBaseModule {
    }

    //
    // Bubbles
    // Bubbles (optional feature)
    //

    @WMSingleton
@@ -211,27 +229,8 @@ public abstract class WMShellBaseModule {
        return bubbleController.map((controller) -> controller.asBubbles());
    }

    // Note: Handler needed for LauncherApps.register
    @WMSingleton
    @Provides
    static Optional<BubbleController> provideBubbleController(Context context,
            FloatingContentCoordinator floatingContentCoordinator,
            IStatusBarService statusBarService,
            WindowManager windowManager,
            WindowManagerShellWrapper windowManagerShellWrapper,
            LauncherApps launcherApps,
            TaskStackListenerImpl taskStackListener,
            UiEventLogger uiEventLogger,
            ShellTaskOrganizer organizer,
            DisplayController displayController,
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellMainThread Handler mainHandler,
            SyncTransactionQueue syncQueue) {
        return Optional.of(BubbleController.create(context, null /* synchronizer */,
                floatingContentCoordinator, statusBarService, windowManager,
                windowManagerShellWrapper, launcherApps, taskStackListener,
                uiEventLogger, organizer, displayController, mainExecutor, mainHandler, syncQueue));
    }
    @BindsOptionalOf
    abstract BubbleController optionalBubblesController();

    //
    // Fullscreen
@@ -252,59 +251,45 @@ public abstract class WMShellBaseModule {
    // Unfold transition
    //

    @WMSingleton
    @Provides
    static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
            Context context,
            Optional<ShellUnfoldProgressProvider> progressProvider,
            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
            DisplayInsetsController displayInsetsController,
            @ShellMainThread ShellExecutor mainExecutor
    ) {
        return progressProvider.map(shellUnfoldTransitionProgressProvider ->
                new FullscreenUnfoldController(context, mainExecutor,
                        unfoldBackgroundController.get(), shellUnfoldTransitionProgressProvider,
                        displayInsetsController));
    }
    @BindsOptionalOf
    abstract ShellUnfoldProgressProvider optionalShellUnfoldProgressProvider();

    @Provides
    static Optional<StageTaskUnfoldController> provideStageTaskUnfoldController(
            Optional<ShellUnfoldProgressProvider> progressProvider,
            Context context,
            TransactionPool transactionPool,
            Lazy<UnfoldBackgroundController> unfoldBackgroundController,
            DisplayInsetsController displayInsetsController,
            @ShellMainThread ShellExecutor mainExecutor
    ) {
        return progressProvider.map(shellUnfoldTransitionProgressProvider ->
                new StageTaskUnfoldController(
                        context,
                        transactionPool,
                        shellUnfoldTransitionProgressProvider,
                        displayInsetsController,
                        unfoldBackgroundController.get(),
                        mainExecutor
                ));
    }
    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract FullscreenUnfoldController optionalFullscreenUnfoldController();

    @WMSingleton
    @Provides
    static UnfoldBackgroundController provideUnfoldBackgroundController(
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            Context context
    ) {
        return new UnfoldBackgroundController(
                context,
                rootTaskDisplayAreaOrganizer
        );
    static Optional<FullscreenUnfoldController> provideFullscreenUnfoldController(
            @DynamicOverride Optional<FullscreenUnfoldController> fullscreenUnfoldController,
            Optional<ShellUnfoldProgressProvider> progressProvider) {
        if (progressProvider.isPresent()
                && progressProvider.get() != ShellUnfoldProgressProvider.NO_PROVIDER) {
            return fullscreenUnfoldController;
        }
        return Optional.empty();
    }

    //
    // Freeform (optional feature)
    //

    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    abstract Optional<FreeformTaskListener> optionalFreeformTaskListener();
    @DynamicOverride
    abstract FreeformTaskListener optionalFreeformTaskListener();

    @WMSingleton
    @Provides
    static Optional<FreeformTaskListener> provideFreeformTaskListener(
            @DynamicOverride Optional<FreeformTaskListener> freeformTaskListener,
            Context context) {
        if (FreeformTaskListener.isFreeformEnabled(context)) {
            return freeformTaskListener;
        }
        return Optional.empty();
    }

    //
    // Hide display cutout
@@ -335,19 +320,21 @@ public abstract class WMShellBaseModule {
        return oneHandedController.map((controller) -> controller.asOneHanded());
    }

    // Needs the shell main handler for ContentObserver callbacks
    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract OneHandedController optionalOneHandedController();

    @WMSingleton
    @Provides
    static Optional<OneHandedController> provideOneHandedController(Context context,
            WindowManager windowManager, DisplayController displayController,
            DisplayLayout displayLayout, TaskStackListenerImpl taskStackListener,
            UiEventLogger uiEventLogger,
            @ShellMainThread ShellExecutor mainExecutor,
            @ShellMainThread Handler mainHandler) {
        return Optional.ofNullable(OneHandedController.create(context, windowManager,
                displayController, displayLayout, taskStackListener, uiEventLogger, mainExecutor,
                mainHandler));
    static Optional<OneHandedController> providesOneHandedController(
            @DynamicOverride Optional<OneHandedController> oneHandedController) {
        if (!SystemProperties.getBoolean(SUPPORT_ONE_HANDED_MODE, false)) {
            return oneHandedController;
        }
        return Optional.empty();
    }


    //
    // Task to Surface communication
@@ -366,15 +353,6 @@ public abstract class WMShellBaseModule {
        return Optional.ofNullable(new TaskSurfaceHelperController(taskOrganizer, mainExecutor));
    }

    @WMSingleton
    @Provides
    static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
            @ShellMainThread ShellExecutor mainExecutor,
            RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
        return Optional.ofNullable(new DisplayAreaHelperController(mainExecutor,
                rootDisplayAreaOrganizer));
    }

    //
    // Pip (optional feature)
    //
@@ -460,7 +438,7 @@ public abstract class WMShellBaseModule {
    }

    //
    // Split/multiwindow
    // Display areas
    //

    @WMSingleton
@@ -477,6 +455,19 @@ public abstract class WMShellBaseModule {
        return new RootDisplayAreaOrganizer(mainExecutor);
    }

    @WMSingleton
    @Provides
    static Optional<DisplayAreaHelper> provideDisplayAreaHelper(
            @ShellMainThread ShellExecutor mainExecutor,
            RootDisplayAreaOrganizer rootDisplayAreaOrganizer) {
        return Optional.of(new DisplayAreaHelperController(mainExecutor,
                rootDisplayAreaOrganizer));
    }

    //
    // Splitscreen (optional feature)
    //

    @WMSingleton
    @Provides
    static Optional<SplitScreen> provideSplitScreen(
@@ -484,26 +475,20 @@ public abstract class WMShellBaseModule {
        return splitScreenController.map((controller) -> controller.asSplitScreen());
    }

    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract SplitScreenController optionalSplitScreenController();

    @WMSingleton
    @Provides
    static Optional<SplitScreenController> provideSplitScreenController(
            ShellTaskOrganizer shellTaskOrganizer,
            SyncTransactionQueue syncQueue, Context context,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            @ShellMainThread ShellExecutor mainExecutor,
            DisplayImeController displayImeController,
            DisplayInsetsController displayInsetsController, Transitions transitions,
            TransactionPool transactionPool, IconProvider iconProvider,
            Optional<RecentTasksController> recentTasks,
            Provider<Optional<StageTaskUnfoldController>> stageTaskUnfoldControllerProvider) {
    static Optional<SplitScreenController> providesSplitScreenController(
            @DynamicOverride Optional<SplitScreenController> splitscreenController,
            Context context) {
        if (ActivityTaskManager.supportsSplitScreenMultiWindow(context)) {
            return Optional.of(new SplitScreenController(shellTaskOrganizer, syncQueue, context,
                    rootTaskDisplayAreaOrganizer, mainExecutor, displayImeController,
                    displayInsetsController, transitions, transactionPool, iconProvider,
                    recentTasks, stageTaskUnfoldControllerProvider));
        } else {
            return Optional.empty();
            return splitscreenController;
        }
        return Optional.empty();
    }

    // Legacy split (optional feature)
@@ -529,7 +514,9 @@ public abstract class WMShellBaseModule {
    @BindsOptionalOf
    abstract AppPairsController optionalAppPairs();

    //
    // Starting window
    //

    @WMSingleton
    @Provides
@@ -548,6 +535,23 @@ public abstract class WMShellBaseModule {
                startingWindowTypeAlgorithm, iconProvider, pool);
    }

    // Workaround for dynamic overriding with a default implementation, see {@link DynamicOverride}
    @BindsOptionalOf
    @DynamicOverride
    abstract StartingWindowTypeAlgorithm optionalStartingWindowTypeAlgorithm();

    @WMSingleton
    @Provides
    static StartingWindowTypeAlgorithm provideStartingWindowTypeAlgorithm(
            @DynamicOverride Optional<StartingWindowTypeAlgorithm> startingWindowTypeAlgorithm
    ) {
        if (startingWindowTypeAlgorithm.isPresent()) {
            return startingWindowTypeAlgorithm.get();
        }
        // Default to phone starting window type
        return new PhoneStartingWindowTypeAlgorithm();
    }

    //
    // Task view factory
    //
@@ -591,7 +595,7 @@ public abstract class WMShellBaseModule {
            Optional<PipTouchHandler> pipTouchHandlerOptional,
            FullscreenTaskListener fullscreenTaskListener,
            Optional<FullscreenUnfoldController> appUnfoldTransitionController,
            Optional<Optional<FreeformTaskListener>> freeformTaskListener,
            Optional<FreeformTaskListener> freeformTaskListener,
            Optional<RecentTasksController> recentTasksOptional,
            Transitions transitions,
            StartingWindowController startingWindow,
+121 −16

File changed.

Preview size limit exceeded, changes collapsed.

Loading