Merge changes I3460e27e,Ib2f1d194,I28b8f266 into udc-dev

* changes:
  Always close the handle menu if the window loses focus
  Delegate window decor view binding / updating to *ViewHolders
  Update desktop mode caption bar to newest spec
This commit is contained in:
Jorge Gil
2023-03-21 17:37:28 +00:00
committed by Android (Google) Code Review
12 changed files with 375 additions and 166 deletions

View File

@@ -14,13 +14,12 @@
~ limitations under the License.
-->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="@color/decor_button_dark_color">
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<group android:translateY="8.0">
<path
android:fillColor="@android:color/white" android:pathData="M3,5V3H21V5Z"/>
android:fillColor="@android:color/black" android:pathData="M3,5V3H21V5Z"/>
</group>
</vector>

View File

@@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (C) 2023 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
-->
<vector android:height="24dp" android:tint="#000000"
android:viewportHeight="24" android:viewportWidth="24"
android:width="24dp" xmlns:android="http://schemas.android.com/apk/res/android">
<path android:fillColor="@android:color/black" android:pathData="M16.59,8.59L12,13.17 7.41,8.59 6,10l6,6 6,-6z"/>
</vector>

View File

@@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ 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.
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/desktop_mode_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="horizontal"
android:background="@drawable/desktop_mode_decor_title">
<LinearLayout
android:id="@+id/open_menu_button"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:orientation="horizontal"
android:clickable="true"
android:focusable="true"
android:paddingStart="8dp"
android:background="?android:selectableItemBackgroundBorderless">
<ImageView
android:id="@+id/application_icon"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_margin="4dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/app_icon_text" />
<TextView
android:id="@+id/application_name"
android:layout_width="0dp"
android:layout_height="match_parent"
android:minWidth="80dp"
android:textColor="@color/desktop_mode_caption_app_name_dark"
android:textSize="14sp"
android:textFontWeight="500"
android:gravity="center_vertical"
android:layout_weight="1"
android:paddingStart="4dp"
android:paddingEnd="4dp"
tools:text="Gmail"/>
<ImageButton
android:id="@+id/expand_menu_button"
android:layout_width="32dp"
android:layout_height="32dp"
android:padding="4dp"
android:contentDescription="@string/collapse_menu_text"
android:src="@drawable/ic_baseline_expand_more_24"
android:tint="@color/desktop_mode_caption_expand_button_dark"
android:background="@null"
android:scaleType="fitCenter"
android:clickable="false"
android:focusable="false"
android:layout_gravity="center_vertical"/>
</LinearLayout>
<View
android:id="@+id/caption_handle"
android:layout_width="wrap_content"
android:layout_height="40dp"
android:layout_weight="1"/>
<ImageButton
android:id="@+id/close_window"
android:layout_width="40dp"
android:layout_height="40dp"
android:padding="4dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/close_button_text"
android:src="@drawable/decor_close_button_dark"
android:scaleType="fitCenter"
android:gravity="end"
android:background="?android:selectableItemBackgroundBorderless"
android:tint="@color/desktop_mode_caption_close_button_dark"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>

View File

@@ -50,7 +50,7 @@
android:layout_marginEnd="10dp"
android:contentDescription="@string/collapse_menu_text"
android:layout_alignParentEnd="true"
android:background="@drawable/caption_collapse_menu_button"
android:background="@drawable/ic_baseline_expand_more_24"
android:layout_centerVertical="true"/>
</RelativeLayout>
<LinearLayout

View File

@@ -16,26 +16,21 @@
-->
<com.android.wm.shell.windowdecor.WindowDecorLinearLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/desktop_mode_caption"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:background="@drawable/desktop_mode_decor_title">
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/back_button"
android:contentDescription="@string/back_button_text"
android:background="@drawable/decor_back_button_dark"/>
<Button
<ImageButton
android:id="@+id/caption_handle"
android:layout_width="128dp"
android:layout_height="32dp"
android:layout_margin="5dp"
android:layout_height="42dp"
android:contentDescription="@string/handle_text"
android:background="@drawable/decor_handle_dark"/>
<Button
style="@style/CaptionButtonStyle"
android:id="@+id/close_window"
android:contentDescription="@string/close_button_text"
android:background="@drawable/decor_close_button_dark"/>
android:src="@drawable/decor_handle_dark"
tools:tint="@color/desktop_mode_caption_handle_bar_dark"
android:scaleType="fitXY"
android:background="?android:selectableItemBackground"/>
</com.android.wm.shell.windowdecor.WindowDecorLinearLayout>

View File

@@ -54,4 +54,14 @@
<color name="splash_screen_bg_light">#FFFFFF</color>
<color name="splash_screen_bg_dark">#000000</color>
<color name="splash_window_background_default">@color/splash_screen_bg_light</color>
<!-- Desktop Mode -->
<color name="desktop_mode_caption_handle_bar_light">#EFF1F2</color>
<color name="desktop_mode_caption_handle_bar_dark">#1C1C17</color>
<color name="desktop_mode_caption_expand_button_light">#EFF1F2</color>
<color name="desktop_mode_caption_expand_button_dark">#48473A</color>
<color name="desktop_mode_caption_close_button_light">#EFF1F2</color>
<color name="desktop_mode_caption_close_button_dark">#1C1C17</color>
<color name="desktop_mode_caption_app_name_light">#EFF1F2</color>
<color name="desktop_mode_caption_app_name_dark">#1C1C17</color>
</resources>

View File

@@ -166,7 +166,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
decoration.relayout(taskInfo);
setupCaptionColor(taskInfo, decoration);
}
@Override
@@ -252,7 +251,7 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
} else if (id == R.id.back_button) {
mTaskOperations.injectBackKey();
} else if (id == R.id.caption_handle) {
} else if (id == R.id.caption_handle || id == R.id.open_menu_button) {
decoration.createHandleMenu();
} else if (id == R.id.desktop_button) {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(true));
@@ -262,7 +261,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
mDesktopModeController.ifPresent(c -> c.setDesktopModeActive(false));
mDesktopTasksController.ifPresent(c -> c.moveToFullscreen(mTaskId));
decoration.closeHandleMenu();
decoration.setButtonVisibility(false);
} else if (id == R.id.collapse_menu_button) {
decoration.closeHandleMenu();
}
@@ -556,13 +554,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
}
}
private void setupCaptionColor(RunningTaskInfo taskInfo,
DesktopModeWindowDecoration decoration) {
if (taskInfo == null || taskInfo.taskDescription == null) return;
final int statusBarColor = taskInfo.taskDescription.getStatusBarColor();
decoration.setCaptionColor(statusBarColor);
}
private boolean shouldShowWindowDecor(RunningTaskInfo taskInfo) {
if (taskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM) return true;
return DesktopModeStatus.isProto2Enabled()
@@ -602,7 +593,6 @@ public class DesktopModeWindowDecorViewModel implements WindowDecorViewModel {
windowDecoration.setDragPositioningCallback(taskPositioner);
windowDecoration.setDragDetector(touchEventListener.mDragDetector);
windowDecoration.relayout(taskInfo, startT, finishT);
setupCaptionColor(taskInfo, windowDecoration);
incrementEventReceiverTasks(taskInfo.displayId);
}

View File

@@ -22,15 +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.Drawable;
import android.graphics.drawable.GradientDrawable;
import android.graphics.drawable.VectorDrawable;
import android.os.Handler;
import android.util.Log;
import android.view.Choreographer;
@@ -48,20 +43,24 @@ 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
* {@link DesktopModeWindowDecorViewModel}. The caption bar contains a handle, back button, and
* close button.
* {@link DesktopModeWindowDecorViewModel}.
*
* The shadow's thickness is 20dp when the window is in focus and 5dp when the window isn't.
*/
public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
private static final String TAG = "DesktopModeWindowDecoration";
private final Handler mHandler;
private final Choreographer mChoreographer;
private final SyncTransactionQueue mSyncQueue;
private DesktopModeWindowDecorationViewHolder mWindowDecorViewHolder;
private View.OnClickListener mOnCaptionButtonClickListener;
private View.OnTouchListener mOnCaptionTouchListener;
private DragPositioningCallback mDragPositioningCallback;
@@ -73,7 +72,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
private final WindowDecoration.RelayoutResult<WindowDecorLinearLayout> mResult =
new WindowDecoration.RelayoutResult<>();
private boolean mDesktopActive;
private AdditionalWindow mHandleMenu;
private final int mHandleMenuWidthId = R.dimen.freeform_decor_caption_menu_width;
private final int mHandleMenuShadowRadiusId = R.dimen.caption_menu_shadow_radius;
@@ -94,7 +92,6 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
mHandler = handler;
mChoreographer = choreographer;
mSyncQueue = syncQueue;
mDesktopActive = DesktopModeStatus.isActive(mContext);
}
@Override
@@ -152,9 +149,11 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int outsetRightId = R.dimen.freeform_resize_handle;
final int outsetBottomId = R.dimen.freeform_resize_handle;
final int windowDecorLayoutId = getDesktopModeWindowDecorLayoutId(
taskInfo.getWindowingMode());
mRelayoutParams.reset();
mRelayoutParams.mRunningTaskInfo = taskInfo;
mRelayoutParams.mLayoutResId = R.layout.desktop_mode_window_decor;
mRelayoutParams.mLayoutResId = windowDecorLayoutId;
mRelayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
mRelayoutParams.mShadowRadiusId = shadowRadiusID;
if (isDragResizeable) {
@@ -172,24 +171,28 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
return;
}
if (oldRootView != mResult.mRootView) {
setupRootView();
}
// If this task is not focused, do not show caption.
setCaptionVisibility(mTaskInfo.isFocused);
if (mTaskInfo.isFocused) {
if (DesktopModeStatus.isProto2Enabled()) {
updateButtonVisibility();
} else if (DesktopModeStatus.isProto1Enabled()) {
// Only handle should show if Desktop Mode is inactive.
boolean desktopCurrentStatus = DesktopModeStatus.isActive(mContext);
if (mDesktopActive != desktopCurrentStatus) {
mDesktopActive = desktopCurrentStatus;
setButtonVisibility(mDesktopActive);
}
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");
}
}
mWindowDecorViewHolder.bindData(mTaskInfo);
if (!mTaskInfo.isFocused) {
closeHandleMenu();
}
if (!isDragResizeable) {
closeDragResizeListener();
@@ -219,24 +222,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);
handle.setOnClickListener(mOnCaptionButtonClickListener);
if (DesktopModeStatus.isProto1Enabled()) {
final View back = caption.findViewById(R.id.back_button);
back.setOnClickListener(mOnCaptionButtonClickListener);
final View close = caption.findViewById(R.id.close_window);
close.setOnClickListener(mOnCaptionButtonClickListener);
}
updateButtonVisibility();
}
private void setupHandleMenu() {
final View menu = mHandleMenu.mWindowViewHost.getView();
final View fullscreen = menu.findViewById(R.id.fullscreen_button);
@@ -255,98 +240,26 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
collapse.setOnClickListener(mOnCaptionButtonClickListener);
menu.setOnTouchListener(mOnCaptionTouchListener);
String packageName = mTaskInfo.baseActivity.getPackageName();
PackageManager pm = mContext.getApplicationContext().getPackageManager();
// TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
try {
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
PackageManager.ApplicationInfoFlags.of(0));
final ImageView appIcon = menu.findViewById(R.id.application_icon);
appIcon.setImageDrawable(pm.getApplicationIcon(applicationInfo));
final TextView appName = menu.findViewById(R.id.application_name);
appName.setText(pm.getApplicationLabel(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package not found: " + packageName, e);
}
}
/**
* Sets caption visibility based on task focus.
* Note: Only applicable to Desktop Proto 1; Proto 2 only closes handle menu on focus loss
* @param visible whether or not the caption should be visible
*/
private void setCaptionVisibility(boolean visible) {
if (!visible) closeHandleMenu();
if (!DesktopModeStatus.isProto1Enabled()) return;
final int v = visible ? View.VISIBLE : View.GONE;
final View captionView = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
captionView.setVisibility(v);
}
/**
* Sets the visibility of buttons and color of caption based on desktop mode status
*/
void updateButtonVisibility() {
if (DesktopModeStatus.isProto2Enabled()) {
setButtonVisibility(mTaskInfo.getWindowingMode() == WINDOWING_MODE_FREEFORM);
} else if (DesktopModeStatus.isProto1Enabled()) {
mDesktopActive = DesktopModeStatus.isActive(mContext);
setButtonVisibility(mDesktopActive);
}
}
/**
* Show or hide buttons
*/
void setButtonVisibility(boolean visible) {
final int visibility = visible && DesktopModeStatus.isProto1Enabled()
? View.VISIBLE : View.GONE;
final View caption = mResult.mRootView.findViewById(R.id.desktop_mode_caption);
final View back = caption.findViewById(R.id.back_button);
final View close = caption.findViewById(R.id.close_window);
back.setVisibility(visibility);
close.setVisibility(visibility);
final int buttonTintColorRes =
mDesktopActive ? R.color.decor_button_dark_color
: R.color.decor_button_light_color;
final ColorStateList buttonTintColor =
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
final View handle = caption.findViewById(R.id.caption_handle);
final VectorDrawable handleBackground = (VectorDrawable) handle.getBackground();
handleBackground.setTintList(buttonTintColor);
final ImageView appIcon = menu.findViewById(R.id.application_icon);
final TextView appName = menu.findViewById(R.id.application_name);
loadAppInfo(appName, appIcon);
}
boolean isHandleMenuActive() {
return mHandleMenu != null;
}
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 int buttonTintColorRes =
Color.valueOf(captionColor).luminance() < 0.5
? R.color.decor_button_light_color
: R.color.decor_button_dark_color;
final ColorStateList buttonTintColor =
caption.getResources().getColorStateList(buttonTintColorRes, null /* theme */);
final View handle = caption.findViewById(R.id.caption_handle);
final Drawable handleBackground = handle.getBackground();
handleBackground.setTintList(buttonTintColor);
if (DesktopModeStatus.isProto1Enabled()) {
final View back = caption.findViewById(R.id.back_button);
final Drawable backBackground = back.getBackground();
backBackground.setTintList(buttonTintColor);
final View close = caption.findViewById(R.id.close_window);
final Drawable closeBackground = close.getBackground();
closeBackground.setTintList(buttonTintColor);
private void loadAppInfo(TextView appNameTextView, ImageView appIconImageView) {
String packageName = mTaskInfo.realActivity.getPackageName();
PackageManager pm = mContext.getApplicationContext().getPackageManager();
try {
// TODO(b/268363572): Use IconProvider or BaseIconCache to set drawable/name.
ApplicationInfo applicationInfo = pm.getApplicationInfo(packageName,
PackageManager.ApplicationInfoFlags.of(0));
appNameTextView.setText(pm.getApplicationLabel(applicationInfo));
appIconImageView.setImageDrawable(pm.getApplicationIcon(applicationInfo));
} catch (PackageManager.NameNotFoundException e) {
Log.w(TAG, "Package not found: " + packageName, e);
}
}
@@ -371,9 +284,18 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
final int shadowRadius = loadDimensionPixelSize(resources, mHandleMenuShadowRadiusId);
final int cornerRadius = loadDimensionPixelSize(resources, mHandleMenuCornerRadiusId);
final int x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
- mResult.mDecorContainerOffsetX;
final int y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
final int x, y;
if (mRelayoutParams.mLayoutResId
== R.layout.desktop_mode_app_controls_window_decor) {
// Align the handle menu to the left of the caption.
x = mRelayoutParams.mCaptionX - mResult.mDecorContainerOffsetX;
y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
} else {
// Position the handle menu at the center of the caption.
x = mRelayoutParams.mCaptionX + (captionWidth / 2) - (menuWidth / 2)
- mResult.mDecorContainerOffsetX;
y = mRelayoutParams.mCaptionY - mResult.mDecorContainerOffsetY;
}
mHandleMenuPosition.set(x, y);
String namePrefix = "Caption Menu";
mHandleMenu = addWindow(R.layout.desktop_mode_decor_handle_menu, namePrefix, t, x, y,
@@ -503,6 +425,15 @@ public class DesktopModeWindowDecoration extends WindowDecoration<WindowDecorLin
super.close();
}
private int getDesktopModeWindowDecorLayoutId(int windowingMode) {
if (DesktopModeStatus.isProto1Enabled()) {
return R.layout.desktop_mode_app_controls_window_decor;
}
return windowingMode == WINDOWING_MODE_FREEFORM
? R.layout.desktop_mode_app_controls_window_decor
: R.layout.desktop_mode_focused_window_decor;
}
static class Factory {
DesktopModeWindowDecoration create(

View File

@@ -84,6 +84,7 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
};
RunningTaskInfo mTaskInfo;
int mLayoutResId;
final SurfaceControl mTaskSurface;
Display mDisplay;
@@ -162,6 +163,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
if (params.mRunningTaskInfo != null) {
mTaskInfo = params.mRunningTaskInfo;
}
final int oldLayoutResId = mLayoutResId;
mLayoutResId = params.mLayoutResId;
if (!mTaskInfo.isVisible) {
releaseViews();
@@ -178,7 +181,8 @@ public abstract class WindowDecoration<T extends View & TaskFocusStateConsumer>
final Configuration taskConfig = getConfigurationWithOverrides(mTaskInfo);
if (oldTaskConfig.densityDpi != taskConfig.densityDpi
|| mDisplay == null
|| mDisplay.getDisplayId() != mTaskInfo.displayId) {
|| mDisplay.getDisplayId() != mTaskInfo.displayId
|| oldLayoutResId != mLayoutResId) {
releaseViews();
if (!obtainDisplayOrRegisterListener()) {

View File

@@ -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"
}
}

View File

@@ -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)
}
}
}

View File

@@ -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
}
}