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
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
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"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
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
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user