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

Commit afa31d32 authored by Jorge Gil's avatar Jorge Gil
Browse files

Load window decor's app name/icon in the bg thread

Adds a WindowDecorTaskResourceLoader class that loads and caches the
app's resources shared across window decoration UI (App Header, menus,
resize veil, etc). The cached resources are coupled to the window
decor's lifecycle and user changes.
The loader util itself is synchronous, so callers are the ones managing
the switching to the bg thread and back.

Bug: 360452034
Flag: EXEMPT refactor
Test: manual - window decor UI loads as intended, perfetto trace shows
binder calls for ActivityInfo and Drawable loading are done in shell.bg

Change-Id: If85ee04d150467471741ac163fd558b0861d555d
parent 70778387
Loading
Loading
Loading
Loading
+26 −7
Original line number Diff line number Diff line
@@ -17,7 +17,6 @@
package com.android.wm.shell.apptoweb

import android.app.ActivityManager.RunningTaskInfo
import android.app.TaskInfo
import android.content.Context
import android.content.pm.verify.domain.DomainVerificationManager
import android.graphics.Bitmap
@@ -36,8 +35,17 @@ import android.widget.TextView
import android.window.TaskConstants
import com.android.wm.shell.R
import com.android.wm.shell.common.DisplayController
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewHostViewContainer
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import java.util.function.Supplier
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext


/**
@@ -45,13 +53,14 @@ import java.util.function.Supplier
 */
internal class OpenByDefaultDialog(
    private val context: Context,
    private val taskInfo: TaskInfo,
    private val taskInfo: RunningTaskInfo,
    private val taskSurface: SurfaceControl,
    private val displayController: DisplayController,
    private val taskResourceLoader: WindowDecorTaskResourceLoader,
    private val surfaceControlTransactionSupplier: Supplier<SurfaceControl.Transaction>,
    @ShellMainThread private val mainDispatcher: MainCoroutineDispatcher,
    @ShellBackgroundThread private val bgScope: CoroutineScope,
    private val listener: DialogLifecycleListener,
    appIconBitmap: Bitmap?,
    appName: CharSequence?
) {
    private lateinit var dialog: OpenByDefaultDialogView
    private lateinit var viewHost: SurfaceControlViewHost
@@ -67,11 +76,20 @@ internal class OpenByDefaultDialog(
        context.getSystemService(DomainVerificationManager::class.java)!!
    private val packageName = taskInfo.baseActivity?.packageName!!

    private var loadAppInfoJob: Job? = null

    init {
        createDialog()
        initializeRadioButtons()
        bindAppInfo(appIconBitmap, appName)
        loadAppInfoJob = bgScope.launch {
            if (!isActive) return@launch
            val name = taskResourceLoader.getName(taskInfo)
            val icon = taskResourceLoader.getHeaderIcon(taskInfo)
            withContext(mainDispatcher.immediate) {
                if (!isActive) return@withContext
                bindAppInfo(icon, name)
            }
        }
    }

    /** Creates an open by default settings dialog. */
@@ -147,14 +165,15 @@ internal class OpenByDefaultDialog(
    }

    private fun closeMenu() {
        loadAppInfoJob?.cancel()
        dialogContainer?.releaseView()
        dialogContainer = null
        listener.onDialogDismissed()
    }

     private fun bindAppInfo(
        appIconBitmap: Bitmap?,
        appName: CharSequence?
        appIconBitmap: Bitmap,
        appName: CharSequence
    ) {
        appIconView.setImageBitmap(appIconBitmap)
        appNameView.text = appName
+27 −5
Original line number Diff line number Diff line
@@ -151,6 +151,7 @@ import com.android.wm.shell.windowdecor.CaptionWindowDecorViewModel;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecorViewModel;
import com.android.wm.shell.windowdecor.WindowDecorViewModel;
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.DefaultWindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.common.viewhost.PooledWindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
@@ -767,6 +768,8 @@ public abstract class WMShellModule {
    @WMSingleton
    @Provides
    static DesktopTilingDecorViewModel provideDesktopTilingViewModel(Context context,
            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
            @ShellBackgroundThread CoroutineScope bgScope,
            DisplayController displayController,
            RootTaskDisplayAreaOrganizer rootTaskDisplayAreaOrganizer,
            SyncTransactionQueue syncQueue,
@@ -775,9 +778,12 @@ public abstract class WMShellModule {
            ToggleResizeDesktopTaskTransitionHandler toggleResizeDesktopTaskTransitionHandler,
            ReturnToDragStartAnimator returnToDragStartAnimator,
            @DynamicOverride DesktopUserRepositories desktopUserRepositories,
            DesktopModeEventLogger desktopModeEventLogger) {
            DesktopModeEventLogger desktopModeEventLogger,
            WindowDecorTaskResourceLoader windowDecorTaskResourceLoader) {
        return new DesktopTilingDecorViewModel(
                context,
                mainDispatcher,
                bgScope,
                displayController,
                rootTaskDisplayAreaOrganizer,
                syncQueue,
@@ -786,7 +792,8 @@ public abstract class WMShellModule {
                toggleResizeDesktopTaskTransitionHandler,
                returnToDragStartAnimator,
                desktopUserRepositories,
                desktopModeEventLogger
                desktopModeEventLogger,
                windowDecorTaskResourceLoader
        );
    }

@@ -903,6 +910,8 @@ public abstract class WMShellModule {
            @ShellMainThread ShellExecutor shellExecutor,
            @ShellMainThread Handler mainHandler,
            @ShellMainThread Choreographer mainChoreographer,
            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
            @ShellBackgroundThread CoroutineScope bgScope,
            @ShellBackgroundThread ShellExecutor bgExecutor,
            ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
@@ -929,13 +938,15 @@ public abstract class WMShellModule {
            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
            FocusTransitionObserver focusTransitionObserver,
            DesktopModeEventLogger desktopModeEventLogger,
            DesktopModeUiEventLogger desktopModeUiEventLogger
            DesktopModeUiEventLogger desktopModeUiEventLogger,
            WindowDecorTaskResourceLoader taskResourceLoader
    ) {
        if (!DesktopModeStatus.canEnterDesktopModeOrShowAppHandle(context)) {
            return Optional.empty();
        }
        return Optional.of(new DesktopModeWindowDecorViewModel(context, shellExecutor, mainHandler,
                mainChoreographer, bgExecutor, shellInit, shellCommandHandler, windowManager,
                mainChoreographer, mainDispatcher, bgScope, bgExecutor,
                shellInit, shellCommandHandler, windowManager,
                taskOrganizer, desktopUserRepositories, displayController, shellController,
                displayInsetsController, syncQueue, transitions, desktopTasksController,
                desktopImmersiveController.get(),
@@ -943,7 +954,18 @@ public abstract class WMShellModule {
                assistContentRequester, windowDecorViewHostSupplier, multiInstanceHelper,
                desktopTasksLimiter, appHandleEducationController, appToWebEducationController,
                windowDecorCaptionHandleRepository, activityOrientationChangeHandler,
                focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger));
                focusTransitionObserver, desktopModeEventLogger, desktopModeUiEventLogger,
                taskResourceLoader));
    }

    @WMSingleton
    @Provides
    static WindowDecorTaskResourceLoader provideWindowDecorTaskResourceLoader(
            @NonNull Context context, @NonNull ShellInit shellInit,
            @NonNull ShellController shellController,
            @NonNull ShellCommandHandler shellCommandHandler) {
        return new WindowDecorTaskResourceLoader(context, shellInit, shellController,
                shellCommandHandler);
    }

    @WMSingleton
+25 −3
Original line number Diff line number Diff line
@@ -140,6 +140,7 @@ import com.android.wm.shell.sysui.ShellInit;
import com.android.wm.shell.transition.FocusTransitionObserver;
import com.android.wm.shell.transition.Transitions;
import com.android.wm.shell.windowdecor.DesktopModeWindowDecoration.ExclusionRegionListener;
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
import com.android.wm.shell.windowdecor.extension.InsetsStateKt;
@@ -150,7 +151,9 @@ import kotlin.Pair;
import kotlin.Unit;
import kotlin.jvm.functions.Function1;

import kotlinx.coroutines.CoroutineScope;
import kotlinx.coroutines.ExperimentalCoroutinesApi;
import kotlinx.coroutines.MainCoroutineDispatcher;

import java.io.PrintWriter;
import java.util.ArrayList;
@@ -177,6 +180,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
    private final ShellController mShellController;
    private final Context mContext;
    private final @ShellMainThread Handler mMainHandler;
    private final @ShellMainThread MainCoroutineDispatcher mMainDispatcher;
    private final @ShellBackgroundThread CoroutineScope mBgScope;
    private final @ShellBackgroundThread ShellExecutor mBgExecutor;
    private final Choreographer mMainChoreographer;
    private final DisplayController mDisplayController;
@@ -241,12 +246,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
    private final FocusTransitionObserver mFocusTransitionObserver;
    private final DesktopModeEventLogger mDesktopModeEventLogger;
    private final DesktopModeUiEventLogger mDesktopModeUiEventLogger;
    private final WindowDecorTaskResourceLoader mTaskResourceLoader;

    public DesktopModeWindowDecorViewModel(
            Context context,
            ShellExecutor shellExecutor,
            @ShellMainThread Handler mainHandler,
            Choreographer mainChoreographer,
            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
            @ShellBackgroundThread CoroutineScope bgScope,
            @ShellBackgroundThread ShellExecutor bgExecutor,
            ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
@@ -273,12 +281,15 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
            Optional<DesktopActivityOrientationChangeHandler> activityOrientationChangeHandler,
            FocusTransitionObserver focusTransitionObserver,
            DesktopModeEventLogger desktopModeEventLogger,
            DesktopModeUiEventLogger desktopModeUiEventLogger) {
            DesktopModeUiEventLogger desktopModeUiEventLogger,
            WindowDecorTaskResourceLoader taskResourceLoader) {
        this(
                context,
                shellExecutor,
                mainHandler,
                mainChoreographer,
                mainDispatcher,
                bgScope,
                bgExecutor,
                shellInit,
                shellCommandHandler,
@@ -311,7 +322,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                new TaskPositionerFactory(),
                focusTransitionObserver,
                desktopModeEventLogger,
                desktopModeUiEventLogger);
                desktopModeUiEventLogger,
                taskResourceLoader);
    }

    @VisibleForTesting
@@ -320,6 +332,8 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
            ShellExecutor shellExecutor,
            @ShellMainThread Handler mainHandler,
            Choreographer mainChoreographer,
            @ShellMainThread MainCoroutineDispatcher mainDispatcher,
            @ShellBackgroundThread CoroutineScope bgScope,
            @ShellBackgroundThread ShellExecutor bgExecutor,
            ShellInit shellInit,
            ShellCommandHandler shellCommandHandler,
@@ -352,11 +366,14 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
            TaskPositionerFactory taskPositionerFactory,
            FocusTransitionObserver focusTransitionObserver,
            DesktopModeEventLogger desktopModeEventLogger,
            DesktopModeUiEventLogger desktopModeUiEventLogger) {
            DesktopModeUiEventLogger desktopModeUiEventLogger,
            WindowDecorTaskResourceLoader taskResourceLoader) {
        mContext = context;
        mMainExecutor = shellExecutor;
        mMainHandler = mainHandler;
        mMainChoreographer = mainChoreographer;
        mMainDispatcher = mainDispatcher;
        mBgScope = bgScope;
        mBgExecutor = bgExecutor;
        mActivityTaskManager = mContext.getSystemService(ActivityTaskManager.class);
        mTaskOrganizer = taskOrganizer;
@@ -418,6 +435,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
        mFocusTransitionObserver = focusTransitionObserver;
        mDesktopModeEventLogger = desktopModeEventLogger;
        mDesktopModeUiEventLogger = desktopModeUiEventLogger;
        mTaskResourceLoader = taskResourceLoader;

        shellInit.addInitCallback(this::onInit, this);
    }
@@ -1640,12 +1658,16 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel,
                                : mContext,
                        mContext.createContextAsUser(UserHandle.of(taskInfo.userId), 0 /* flags */),
                        mDisplayController,
                        mTaskResourceLoader,
                        mSplitScreenController,
                        mDesktopUserRepositories,
                        mTaskOrganizer,
                        taskInfo,
                        taskSurface,
                        mMainHandler,
                        mMainExecutor,
                        mMainDispatcher,
                        mBgScope,
                        mBgExecutor,
                        mMainChoreographer,
                        mSyncQueue,
+97 −76

File changed.

Preview size limit exceeded, changes collapsed.

+55 −26
Original line number Diff line number Diff line
@@ -47,15 +47,25 @@ import androidx.compose.ui.graphics.toArgb
import androidx.core.view.isGone
import com.android.window.flags.Flags
import com.android.wm.shell.R
import com.android.wm.shell.shared.annotations.ShellBackgroundThread
import com.android.wm.shell.shared.annotations.ShellMainThread
import com.android.wm.shell.shared.split.SplitScreenConstants
import com.android.wm.shell.splitscreen.SplitScreenController
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalSystemViewContainer
import com.android.wm.shell.windowdecor.additionalviewcontainer.AdditionalViewContainer
import com.android.wm.shell.windowdecor.common.DecorThemeUtil
import com.android.wm.shell.windowdecor.common.calculateMenuPosition
import com.android.wm.shell.windowdecor.common.WindowDecorTaskResourceLoader
import com.android.wm.shell.windowdecor.extension.isFullscreen
import com.android.wm.shell.windowdecor.extension.isMultiWindow
import com.android.wm.shell.windowdecor.extension.isPinned
import kotlinx.coroutines.CoroutineDispatcher
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.MainCoroutineDispatcher
import kotlinx.coroutines.isActive
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext

/**
 * Handle menu opened when the appropriate button is clicked on.
@@ -66,11 +76,12 @@ import com.android.wm.shell.windowdecor.extension.isPinned
 * Additional Options: Miscellaneous functions including screenshot and closing task.
 */
class HandleMenu(
    @ShellMainThread private val mainDispatcher: CoroutineDispatcher,
    @ShellBackgroundThread private val bgScope: CoroutineScope,
    private val parentDecor: DesktopModeWindowDecoration,
    private val windowManagerWrapper: WindowManagerWrapper,
    private val taskResourceLoader: WindowDecorTaskResourceLoader,
    private val layoutResId: Int,
    private val appIconBitmap: Bitmap?,
    private val appName: CharSequence?,
    private val splitScreenController: SplitScreenController,
    private val shouldShowWindowingPill: Boolean,
    private val shouldShowNewWindowButton: Boolean,
@@ -103,7 +114,8 @@ class HandleMenu(

    @VisibleForTesting
    var handleMenuViewContainer: AdditionalViewContainer? = null
    private var handleMenuView: HandleMenuView? = null
    @VisibleForTesting
    var handleMenuView: HandleMenuView? = null

    // Position of the handle menu used for laying out the handle view.
    @VisibleForTesting
@@ -122,6 +134,8 @@ class HandleMenu(
        get() = SHOULD_SHOW_SCREENSHOT_BUTTON || shouldShowNewWindowButton ||
            shouldShowManageWindowsButton || shouldShowChangeAspectRatioButton

    private var loadAppInfoJob: Job? = null

    init {
        updateHandleMenuPillPositions(captionX, captionY)
    }
@@ -190,7 +204,7 @@ class HandleMenu(
            shouldShowDesktopModeButton = shouldShowDesktopModeButton,
            isBrowserApp = isBrowserApp
        ).apply {
            bind(taskInfo, appIconBitmap, appName, shouldShowMoreActionsPill)
            bind(taskInfo, shouldShowMoreActionsPill)
            this.onToDesktopClickListener = onToDesktopClickListener
            this.onToFullscreenClickListener = onToFullscreenClickListener
            this.onToSplitScreenClickListener = onToSplitScreenClickListener
@@ -204,7 +218,16 @@ class HandleMenu(
            this.onCloseMenuClickListener = onCloseMenuClickListener
            this.onOutsideTouchListener = onOutsideTouchListener
        }

        loadAppInfoJob = bgScope.launch {
            if (!isActive) return@launch
            val name = taskResourceLoader.getName(taskInfo)
            val icon = taskResourceLoader.getHeaderIcon(taskInfo)
            withContext(mainDispatcher) {
                if (!isActive) return@withContext
                handleMenuView.setAppName(name)
                handleMenuView.setAppIcon(icon)
            }
        }
        val x = handleMenuPosition.x.toInt()
        val y = handleMenuPosition.y.toInt()
        handleMenuViewContainer =
@@ -412,6 +435,7 @@ class HandleMenu(
        resources.configuration.layoutDirection == View.LAYOUT_DIRECTION_RTL

    fun close() {
        loadAppInfoJob?.cancel()
        handleMenuView?.animateCloseMenu {
            handleMenuViewContainer?.releaseView()
            handleMenuViewContainer = null
@@ -439,8 +463,10 @@ class HandleMenu(
        private val appInfoPill = rootView.requireViewById<View>(R.id.app_info_pill)
        private val collapseMenuButton = appInfoPill.requireViewById<HandleMenuImageButton>(
            R.id.collapse_menu_button)
        private val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
        private val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)
        @VisibleForTesting
        val appIconView = appInfoPill.requireViewById<ImageView>(R.id.application_icon)
        @VisibleForTesting
        val appNameView = appInfoPill.requireViewById<TextView>(R.id.application_name)

        // Windowing Pill.
        private val windowingPill = rootView.requireViewById<View>(R.id.windowing_pill)
@@ -509,14 +535,12 @@ class HandleMenu(
        /** Binds the menu views to the new data. */
        fun bind(
            taskInfo: RunningTaskInfo,
            appIconBitmap: Bitmap?,
            appName: CharSequence?,
            shouldShowMoreActionsPill: Boolean
        ) {
            this.taskInfo = taskInfo
            this.style = calculateMenuStyle(taskInfo)

            bindAppInfoPill(style, appIconBitmap, appName)
            bindAppInfoPill(style)
            if (shouldShowWindowingPill) {
                bindWindowingPill(style)
            }
@@ -527,6 +551,16 @@ class HandleMenu(
            bindOpenInAppOrBrowserPill(style)
        }

        /** Sets the app's name. */
        fun setAppName(name: CharSequence) {
            appNameView.text = name
        }

        /** Sets the app's icon. */
        fun setAppIcon(icon: Bitmap) {
            appIconView.setImageBitmap(icon)
        }

        /** Animates the menu openInAppOrBrowserg. */
        fun animateOpenMenu() {
            if (taskInfo.isFullscreen || taskInfo.isMultiWindow) {
@@ -593,22 +627,14 @@ class HandleMenu(
            )
        }

        private fun bindAppInfoPill(
            style: MenuStyle,
            appIconBitmap: Bitmap?,
            appName: CharSequence?
        ) {
        private fun bindAppInfoPill(style: MenuStyle) {
            appInfoPill.background.setTint(style.backgroundColor)

            collapseMenuButton.apply {
                imageTintList = ColorStateList.valueOf(style.textColor)
                this.taskInfo = this@HandleMenuView.taskInfo
            }
            appIconView.setImageBitmap(appIconBitmap)
            appNameView.apply {
                text = appName
                setTextColor(style.textColor)
            }
            appNameView.setTextColor(style.textColor)
        }

        private fun bindWindowingPill(style: MenuStyle) {
@@ -698,11 +724,12 @@ class HandleMenu(
/** A factory interface to create a [HandleMenu]. */
interface HandleMenuFactory {
    fun create(
        @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
        @ShellBackgroundThread bgScope: CoroutineScope,
        parentDecor: DesktopModeWindowDecoration,
        windowManagerWrapper: WindowManagerWrapper,
        taskResourceLoader: WindowDecorTaskResourceLoader,
        layoutResId: Int,
        appIconBitmap: Bitmap?,
        appName: CharSequence?,
        splitScreenController: SplitScreenController,
        shouldShowWindowingPill: Boolean,
        shouldShowNewWindowButton: Boolean,
@@ -721,11 +748,12 @@ interface HandleMenuFactory {
/** A [HandleMenuFactory] implementation that creates a [HandleMenu].  */
object DefaultHandleMenuFactory : HandleMenuFactory {
    override fun create(
        @ShellMainThread mainDispatcher: MainCoroutineDispatcher,
        @ShellBackgroundThread bgScope: CoroutineScope,
        parentDecor: DesktopModeWindowDecoration,
        windowManagerWrapper: WindowManagerWrapper,
        taskResourceLoader: WindowDecorTaskResourceLoader,
        layoutResId: Int,
        appIconBitmap: Bitmap?,
        appName: CharSequence?,
        splitScreenController: SplitScreenController,
        shouldShowWindowingPill: Boolean,
        shouldShowNewWindowButton: Boolean,
@@ -740,11 +768,12 @@ object DefaultHandleMenuFactory : HandleMenuFactory {
        captionY: Int,
    ): HandleMenu {
        return HandleMenu(
            mainDispatcher,
            bgScope,
            parentDecor,
            windowManagerWrapper,
            taskResourceLoader,
            layoutResId,
            appIconBitmap,
            appName,
            splitScreenController,
            shouldShowWindowingPill,
            shouldShowNewWindowButton,
Loading