| /* |
| * Copyright (C) 2022 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.systemui; |
| |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.os.Handler; |
| import android.os.HandlerThread; |
| import android.util.Log; |
| |
| import com.android.systemui.dagger.GlobalRootComponent; |
| import com.android.systemui.dagger.SysUIComponent; |
| import com.android.systemui.dagger.WMComponent; |
| import com.android.systemui.util.InitializationChecker; |
| import com.android.wm.shell.dagger.WMShellConcurrencyModule; |
| import com.android.wm.shell.keyguard.KeyguardTransitions; |
| import com.android.wm.shell.sysui.ShellInterface; |
| import com.android.wm.shell.transition.ShellTransitions; |
| |
| import java.util.Optional; |
| import java.util.concurrent.ExecutionException; |
| |
| /** |
| * Initializer that stands up SystemUI. |
| * |
| * Implementations should override {@link #getGlobalRootComponentBuilder()} to fill in their own |
| * Dagger root component. |
| */ |
| public abstract class SystemUIInitializer { |
| private static final String TAG = "SystemUIFactory"; |
| |
| private final Context mContext; |
| |
| private GlobalRootComponent mRootComponent; |
| private WMComponent mWMComponent; |
| private SysUIComponent mSysUIComponent; |
| private InitializationChecker mInitializationChecker; |
| |
| public SystemUIInitializer(Context context) { |
| mContext = context; |
| } |
| |
| protected abstract GlobalRootComponent.Builder getGlobalRootComponentBuilder(); |
| |
| /** |
| * Prepares the SysUIComponent builder before it is built. |
| * @param sysUIBuilder the builder provided by the root component's getSysUIComponent() method |
| * @param wm the built WMComponent from the root component's getWMComponent() method |
| */ |
| protected SysUIComponent.Builder prepareSysUIComponentBuilder( |
| SysUIComponent.Builder sysUIBuilder, WMComponent wm) { |
| return sysUIBuilder; |
| } |
| |
| /** |
| * Starts the initialization process. This stands up the Dagger graph. |
| */ |
| public void init(boolean fromTest) throws ExecutionException, InterruptedException { |
| mRootComponent = getGlobalRootComponentBuilder() |
| .context(mContext) |
| .instrumentationTest(fromTest) |
| .build(); |
| |
| mInitializationChecker = mRootComponent.getInitializationChecker(); |
| boolean initializeComponents = mInitializationChecker.initializeComponents(); |
| |
| // Stand up WMComponent |
| setupWmComponent(mContext); |
| |
| // And finally, retrieve whatever SysUI needs from WMShell and build SysUI. |
| SysUIComponent.Builder builder = mRootComponent.getSysUIComponent(); |
| if (initializeComponents) { |
| // Only initialize when not starting from tests since this currently initializes some |
| // components that shouldn't be run in the test environment |
| builder = prepareSysUIComponentBuilder(builder, mWMComponent) |
| .setShell(mWMComponent.getShell()) |
| .setPip(mWMComponent.getPip()) |
| .setSplitScreen(mWMComponent.getSplitScreen()) |
| .setOneHanded(mWMComponent.getOneHanded()) |
| .setBubbles(mWMComponent.getBubbles()) |
| .setTaskViewFactory(mWMComponent.getTaskViewFactory()) |
| .setTransitions(mWMComponent.getTransitions()) |
| .setKeyguardTransitions(mWMComponent.getKeyguardTransitions()) |
| .setStartingSurface(mWMComponent.getStartingSurface()) |
| .setDisplayAreaHelper(mWMComponent.getDisplayAreaHelper()) |
| .setRecentTasks(mWMComponent.getRecentTasks()) |
| .setBackAnimation(mWMComponent.getBackAnimation()) |
| .setDesktopMode(mWMComponent.getDesktopMode()); |
| |
| // Only initialize when not starting from tests since this currently initializes some |
| // components that shouldn't be run in the test environment |
| mWMComponent.init(); |
| } else { |
| // TODO: Call on prepareSysUIComponentBuilder but not with real components. Other option |
| // is separating this logic into newly creating SystemUITestsFactory. |
| builder = prepareSysUIComponentBuilder(builder, mWMComponent) |
| .setShell(new ShellInterface() {}) |
| .setPip(Optional.ofNullable(null)) |
| .setSplitScreen(Optional.ofNullable(null)) |
| .setOneHanded(Optional.ofNullable(null)) |
| .setBubbles(Optional.ofNullable(null)) |
| .setTaskViewFactory(Optional.ofNullable(null)) |
| .setTransitions(new ShellTransitions() {}) |
| .setKeyguardTransitions(new KeyguardTransitions() {}) |
| .setDisplayAreaHelper(Optional.ofNullable(null)) |
| .setStartingSurface(Optional.ofNullable(null)) |
| .setRecentTasks(Optional.ofNullable(null)) |
| .setBackAnimation(Optional.ofNullable(null)) |
| .setDesktopMode(Optional.ofNullable(null)); |
| } |
| mSysUIComponent = builder.build(); |
| if (initializeComponents) { |
| mSysUIComponent.init(); |
| } |
| |
| // Every other part of our codebase currently relies on Dependency, so we |
| // really need to ensure the Dependency gets initialized early on. |
| Dependency dependency = mSysUIComponent.createDependency(); |
| dependency.start(); |
| } |
| |
| /** |
| * Sets up {@link #mWMComponent}. On devices where the Shell runs on its own main thread, |
| * this will pre-create the thread to ensure that the components are constructed on the |
| * same thread, to reduce the likelihood of side effects from running the constructors on |
| * a different thread than the rest of the class logic. |
| */ |
| private void setupWmComponent(Context context) { |
| WMComponent.Builder wmBuilder = mRootComponent.getWMComponentBuilder(); |
| if (!mInitializationChecker.initializeComponents() |
| || !WMShellConcurrencyModule.enableShellMainThread(context)) { |
| // If running under tests or shell thread is not enabled, we don't need anything special |
| mWMComponent = wmBuilder.build(); |
| return; |
| } |
| |
| // If the shell main thread is enabled, initialize the component on that thread |
| HandlerThread shellThread = WMShellConcurrencyModule.createShellMainThread(); |
| shellThread.start(); |
| |
| // Use an async handler since we don't care about synchronization |
| Handler shellHandler = Handler.createAsync(shellThread.getLooper()); |
| boolean built = shellHandler.runWithScissors(() -> { |
| wmBuilder.setShellMainThread(shellThread); |
| mWMComponent = wmBuilder.build(); |
| }, 5000); |
| if (!built) { |
| Log.w(TAG, "Failed to initialize WMComponent"); |
| throw new RuntimeException(); |
| } |
| } |
| |
| public GlobalRootComponent getRootComponent() { |
| return mRootComponent; |
| } |
| |
| public WMComponent getWMComponent() { |
| return mWMComponent; |
| } |
| |
| public SysUIComponent getSysUIComponent() { |
| return mSysUIComponent; |
| } |
| |
| /** |
| * Returns the list of additional system UI components that should be started. |
| */ |
| public String getVendorComponent(Resources resources) { |
| return resources.getString(R.string.config_systemUIVendorServiceComponent); |
| } |
| } |