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

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

Delegate window decor view binding / updating to *ViewHolders

Moves view binding and updates from the new task data in relayout
to a *ViewHolder class made for each "type" of window decoration UI.
This allows caching of findViewById calls and isolated view binding
logic for different UI variants of the caption window decoration.

Bug: 269618335
Test: use proto1/proto2 and switch between modes, no UI/behavior
changes observed

Change-Id: Ib2f1d194af72883653ac93bbd98ae4e9e7b4376a
parent 2628c45e
Loading
Loading
Loading
Loading
+21 −95
Original line number Diff line number Diff line
@@ -22,13 +22,10 @@ import android.app.ActivityManager;
import android.content.Context;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageManager;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Point;
import android.graphics.PointF;
import android.graphics.drawable.GradientDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.Choreographer;
@@ -36,7 +33,6 @@ import android.view.MotionEvent;
import android.view.SurfaceControl;
import android.view.View;
import android.view.ViewConfiguration;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.TextView;
import android.window.WindowContainerTransaction;
@@ -47,6 +43,9 @@ import com.android.wm.shell.common.DisplayController;
import com.android.wm.shell.common.SyncTransactionQueue;
import com.android.wm.shell.desktopmode.DesktopModeStatus;
import com.android.wm.shell.desktopmode.DesktopTasksController;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeAppControlsWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeFocusedWindowDecorationViewHolder;
import com.android.wm.shell.windowdecor.viewholder.DesktopModeWindowDecorationViewHolder;

/**
 * Defines visuals and behaviors of a window decoration of a caption bar and shadows. It works with
@@ -61,6 +60,7 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
    private final Choreographer mChoreographer;
    private final SyncTransactionQueue mSyncQueue;

    private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
    private View.OnClickListener mOnCaptionButtonClickListener;
    private View.OnTouchListener mOnCaptionTouchListener;
    private DragPositioningCallback mDragPositioningCallback;
@@ -171,11 +171,24 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
            return;
        }
        if (oldRootView != mResult.mRootView) {
            setupRootView();
            if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) {
                mWindowDecorViewHolder = new DesktopModeFocusedWindowDecorationViewHolder(
                        mResult.mRootView,
                        mOnCaptionTouchListener,
                        mOnCaptionButtonClickListener
                );
            } else if (mRelayoutParams.mLayoutResId
                    == R.layout.desktop_mode_app_controls_window_decor) {
                mWindowDecorViewHolder = new DesktopModeAppControlsWindowDecorationViewHolder(
                        mResult.mRootView,
                        mOnCaptionTouchListener,
                        mOnCaptionButtonClickListener
                );
            } else {
                throw new IllegalArgumentException("Unexpected layout resource id");
            }

        updateAppInfo();
        setCaptionColor(taskInfo.taskDescription.getStatusBarColor());
        }
        mWindowDecorViewHolder.bindData(mTaskInfo);

        if (!isDragResizeable) {
            closeDragResizeListener();
@@ -205,27 +218,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
                mResult.mWidth, mResult.mHeight, resize_handle, resize_corner, touchSlop);
    }

    /**
     * Sets up listeners when a new root view is created.
     */
    private void setupRootView() {
        final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
        caption.setOnTouchListener(mOnCaptionTouchListener);
        final View handle = caption.findViewById(R.id.caption_handle);
        handle.setOnTouchListener(mOnCaptionTouchListener);
        if (mRelayoutParams.mLayoutResId == R.layout.desktop_mode_focused_window_decor) {
            handle.setOnClickListener(mOnCaptionButtonClickListener);
        } else if (mRelayoutParams.mLayoutResId
                == R.layout.desktop_mode_app_controls_window_decor) {
            caption.findViewById(R.id.open_menu_button)
                    .setOnClickListener(mOnCaptionButtonClickListener);
            caption.findViewById(R.id.close_window)
                    .setOnClickListener(mOnCaptionButtonClickListener);
        } else {
            throw new IllegalArgumentException("Unexpected layout resource id");
        }
    }

    private void setupHandleMenu() {
        final View menu = mHandleMenu.mWindowViewHost.getView();
        final View fullscreen = menu.findViewById(R.id.fullscreen_button);
@@ -249,17 +241,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        loadAppInfo(appName, appIcon);
    }

    private void updateAppInfo() {
        if (mRelayoutParams.mLayoutResId
                != R.layout.desktop_mode_app_controls_window_decor) {
            // The app info views only apply to the app controls window decor type.
            return;
        }
        final TextView appNameTextView = mResult.mRootView.findViewById(R.id.application_name);
        final ImageView appIconImageView = mResult.mRootView.findViewById(R.id.application_icon);
        loadAppInfo(appNameTextView, appIconImageView);
    }

    boolean isHandleMenuActive() {
        return mHandleMenu != null;
    }
@@ -278,61 +259,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
        }
    }

    private void setCaptionColor(int captionColor) {
        if (mResult.mRootView == null) {
            return;
        }

        final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
        final GradientDrawable captionDrawable = (GradientDrawable) caption.getBackground();
        captionDrawable.setColor(captionColor);

        final boolean shouldUseLightCaptionViews = Color.valueOf(captionColor).luminance() < 0.5;
        if (mRelayoutParams.mLayoutResId
                == R.layout.desktop_mode_focused_window_decor) {
            final ImageButton captionBar = caption.findViewById(R.id.caption_handle);
            captionBar.setImageTintList(ColorStateList.valueOf(
                    getCaptionHandleBarColor(shouldUseLightCaptionViews)));
        } else if (mRelayoutParams.mLayoutResId
                == R.layout.desktop_mode_app_controls_window_decor) {
            final ImageButton closeBtn = caption.findViewById(R.id.close_window);
            final ImageButton expandBtn = caption.findViewById(R.id.expand_menu_button);
            final TextView appNameTextView = caption.findViewById(R.id.application_name);
            closeBtn.setImageTintList(ColorStateList.valueOf(
                    getCaptionCloseButtonColor(shouldUseLightCaptionViews)));
            expandBtn.setImageTintList(ColorStateList.valueOf(
                    getCaptionExpandButtonColor(shouldUseLightCaptionViews)));
            appNameTextView.setTextColor(
                    getCaptionAppNameTextColor(shouldUseLightCaptionViews));
        } else {
            throw new IllegalArgumentException("Unexpected layout resource id");
        }
    }

    private int getCaptionHandleBarColor(boolean shouldUseLightCaptionViews) {
        return shouldUseLightCaptionViews
                ? mContext.getColor(R.color.desktop_mode_caption_handle_bar_light)
                : mContext.getColor(R.color.desktop_mode_caption_handle_bar_dark);
    }

    private int getCaptionAppNameTextColor(boolean shouldUseLightCaptionViews) {
        return shouldUseLightCaptionViews
                ? mContext.getColor(R.color.desktop_mode_caption_app_name_light)
                : mContext.getColor(R.color.desktop_mode_caption_app_name_dark);
    }

    private int getCaptionCloseButtonColor(boolean shouldUseLightCaptionViews) {
        return shouldUseLightCaptionViews
                ? mContext.getColor(R.color.desktop_mode_caption_close_button_light)
                : mContext.getColor(R.color.desktop_mode_caption_close_button_dark);
    }

    private int getCaptionExpandButtonColor(boolean shouldUseLightCaptionViews) {
        return shouldUseLightCaptionViews
                ? mContext.getColor(R.color.desktop_mode_caption_expand_button_light)
                : mContext.getColor(R.color.desktop_mode_caption_expand_button_dark);
    }

    private void closeDragResizeListener() {
        if (mDragResizeListener == null) {
            return;
+94 −0
Original line number Diff line number Diff line
package com.android.wm.shell.windowdecor.viewholder

import android.app.ActivityManager.RunningTaskInfo
import android.content.pm.PackageManager
import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.util.Log
import android.view.View
import android.widget.ImageButton
import android.widget.ImageView
import android.widget.TextView
import com.android.wm.shell.R

/**
 * A desktop mode window decoration used when the window is floating (i.e. freeform). It hosts
 * finer controls such as a close window button and an "app info" section to pull up additional
 * controls.
 */
internal class DesktopModeAppControlsWindowDecorationViewHolder(
        rootView: View,
        onCaptionTouchListener: View.OnTouchListener,
        onCaptionButtonClickListener: View.OnClickListener
) : DesktopModeWindowDecorationViewHolder(rootView) {

    private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
    private val captionHandle: View = rootView.findViewById(R.id.caption_handle)
    private val openMenuButton: View = rootView.findViewById(R.id.open_menu_button)
    private val closeWindowButton: ImageButton = rootView.findViewById(R.id.close_window)
    private val expandMenuButton: ImageButton = rootView.findViewById(R.id.expand_menu_button)
    private val appNameTextView: TextView = rootView.findViewById(R.id.application_name)
    private val appIconImageView: ImageView = rootView.findViewById(R.id.application_icon)

    init {
        captionView.setOnTouchListener(onCaptionTouchListener)
        captionHandle.setOnTouchListener(onCaptionTouchListener)
        openMenuButton.setOnClickListener(onCaptionButtonClickListener)
        closeWindowButton.setOnClickListener(onCaptionButtonClickListener)
    }

    override fun bindData(taskInfo: RunningTaskInfo) {
        bindAppInfo(taskInfo)

        val captionDrawable = captionView.background as GradientDrawable
        captionDrawable.setColor(taskInfo.taskDescription.statusBarColor)

        closeWindowButton.imageTintList = ColorStateList.valueOf(
                getCaptionCloseButtonColor(taskInfo))
        expandMenuButton.imageTintList = ColorStateList.valueOf(
                getCaptionExpandButtonColor(taskInfo))
        appNameTextView.setTextColor(getCaptionAppNameTextColor(taskInfo))
    }

    private fun bindAppInfo(taskInfo: RunningTaskInfo) {
        val packageName: String = taskInfo.realActivity.packageName
        val pm: PackageManager = context.applicationContext.packageManager
        try {
            // TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
            val applicationInfo = pm.getApplicationInfo(packageName,
                    PackageManager.ApplicationInfoFlags.of(0))
            appNameTextView.text = pm.getApplicationLabel(applicationInfo)
            appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo))
        } catch (e: PackageManager.NameNotFoundException) {
            Log.w(TAG, "Package not found: $packageName", e)
        }
    }

    private fun getCaptionAppNameTextColor(taskInfo: RunningTaskInfo): Int {
        return if (shouldUseLightCaptionColors(taskInfo)) {
            context.getColor(R.color.desktop_mode_caption_app_name_light)
        } else {
            context.getColor(R.color.desktop_mode_caption_app_name_dark)
        }
    }

    private fun getCaptionCloseButtonColor(taskInfo: RunningTaskInfo): Int {
        return if (shouldUseLightCaptionColors(taskInfo)) {
            context.getColor(R.color.desktop_mode_caption_close_button_light)
        } else {
            context.getColor(R.color.desktop_mode_caption_close_button_dark)
        }
    }

    private fun getCaptionExpandButtonColor(taskInfo: RunningTaskInfo): Int {
        return if (shouldUseLightCaptionColors(taskInfo)) {
            context.getColor(R.color.desktop_mode_caption_expand_button_light)
        } else {
            context.getColor(R.color.desktop_mode_caption_expand_button_dark)
        }
    }

    companion object {
        private const val TAG = "DesktopModeAppControlsWindowDecorationViewHolder"
    }
}
+44 −0
Original line number Diff line number Diff line
package com.android.wm.shell.windowdecor.viewholder

import android.app.ActivityManager.RunningTaskInfo
import android.content.res.ColorStateList
import android.graphics.drawable.GradientDrawable
import android.view.View
import android.widget.ImageButton
import com.android.wm.shell.R

/**
 * A desktop mode window decoration used when the window is in full "focus" (i.e. fullscreen). It
 * hosts a simple handle bar from which to initiate a drag motion to enter desktop mode.
 */
internal class DesktopModeFocusedWindowDecorationViewHolder(
        rootView: View,
        onCaptionTouchListener: View.OnTouchListener,
        onCaptionButtonClickListener: View.OnClickListener
) : DesktopModeWindowDecorationViewHolder(rootView) {

    private val captionView: View = rootView.findViewById(R.id.desktop_mode_caption)
    private val captionHandle: ImageButton = rootView.findViewById(R.id.caption_handle)

    init {
        captionView.setOnTouchListener(onCaptionTouchListener)
        captionHandle.setOnTouchListener(onCaptionTouchListener)
        captionHandle.setOnClickListener(onCaptionButtonClickListener)
    }

    override fun bindData(taskInfo: RunningTaskInfo) {
        val captionColor = taskInfo.taskDescription.statusBarColor
        val captionDrawable = captionView.background as GradientDrawable
        captionDrawable.setColor(captionColor)

        captionHandle.imageTintList = ColorStateList.valueOf(getCaptionHandleBarColor(taskInfo))
    }

    private fun getCaptionHandleBarColor(taskInfo: RunningTaskInfo): Int {
        return if (shouldUseLightCaptionColors(taskInfo)) {
            context.getColor(R.color.desktop_mode_caption_handle_bar_light)
        } else {
            context.getColor(R.color.desktop_mode_caption_handle_bar_dark)
        }
    }
}
+28 −0
Original line number Diff line number Diff line
package com.android.wm.shell.windowdecor.viewholder

import android.app.ActivityManager.RunningTaskInfo
import android.content.Context
import android.graphics.Color
import android.view.View

/**
 * Encapsulates the root [View] of a window decoration and its children to facilitate looking up
 * children (via findViewById) and updating to the latest data from [RunningTaskInfo].
 */
internal abstract class DesktopModeWindowDecorationViewHolder(rootView: View) {
    val context: Context = rootView.context

    /**
     * A signal to the view holder that new data is available and that the views should be updated
     * to reflect it.
     */
    abstract fun bindData(taskInfo: RunningTaskInfo)

    /**
     * Whether the caption items should use the 'light' color variant so that there's good contrast
     * with the caption background color.
     */
    protected fun shouldUseLightCaptionColors(taskInfo: RunningTaskInfo): Boolean {
        return Color.valueOf(taskInfo.taskDescription.statusBarColor).luminance() < 0.5
    }
}