• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2024 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16 package com.android.wm.shell.windowdecor;
17 
18 import static android.view.InsetsSource.FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
19 
20 import android.annotation.SuppressLint;
21 import android.app.ActivityManager;
22 import android.content.Context;
23 import android.graphics.Insets;
24 import android.graphics.Rect;
25 import android.graphics.Region;
26 import android.view.InsetsState;
27 import android.view.SurfaceControl;
28 import android.view.View;
29 import android.view.WindowInsets;
30 import android.window.DesktopModeFlags;
31 import android.window.WindowContainerTransaction;
32 
33 import androidx.annotation.NonNull;
34 
35 import com.android.wm.shell.R;
36 import com.android.wm.shell.ShellTaskOrganizer;
37 import com.android.wm.shell.common.DisplayController;
38 import com.android.wm.shell.common.ShellExecutor;
39 import com.android.wm.shell.shared.annotations.ShellBackgroundThread;
40 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHost;
41 import com.android.wm.shell.windowdecor.common.viewhost.WindowDecorViewHostSupplier;
42 
43 /**
44  * {@link WindowDecoration} to show app controls for windows on automotive.
45  */
46 public class CarWindowDecoration extends WindowDecoration<WindowDecorLinearLayout> {
47     private WindowDecorLinearLayout mRootView;
48     private @ShellBackgroundThread final ShellExecutor mBgExecutor;
49     private final View.OnClickListener mClickListener;
50     private final RelayoutParams mRelayoutParams = new RelayoutParams();
51     private final RelayoutResult<WindowDecorLinearLayout> mResult = new RelayoutResult<>();
52 
CarWindowDecoration( Context context, @android.annotation.NonNull Context userContext, DisplayController displayController, ShellTaskOrganizer taskOrganizer, ActivityManager.RunningTaskInfo taskInfo, SurfaceControl taskSurface, @ShellBackgroundThread ShellExecutor bgExecutor, WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier, View.OnClickListener clickListener)53     CarWindowDecoration(
54             Context context,
55             @android.annotation.NonNull Context userContext,
56             DisplayController displayController,
57             ShellTaskOrganizer taskOrganizer,
58             ActivityManager.RunningTaskInfo taskInfo,
59             SurfaceControl taskSurface,
60             @ShellBackgroundThread ShellExecutor bgExecutor,
61             WindowDecorViewHostSupplier<WindowDecorViewHost> windowDecorViewHostSupplier,
62             View.OnClickListener clickListener) {
63         super(context, userContext, displayController, taskOrganizer, taskInfo, taskSurface,
64                 windowDecorViewHostSupplier);
65         mBgExecutor = bgExecutor;
66         mClickListener = clickListener;
67     }
68 
69     @Override
relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus, @NonNull Region displayExclusionRegion)70     void relayout(ActivityManager.RunningTaskInfo taskInfo, boolean hasGlobalFocus,
71             @NonNull Region displayExclusionRegion) {
72         final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
73         relayout(taskInfo, t, t);
74     }
75 
76     @SuppressLint("MissingPermission")
relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT)77     void relayout(ActivityManager.RunningTaskInfo taskInfo,
78             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT) {
79         relayout(taskInfo, startT, finishT,
80                 /* isCaptionVisible= */ mRelayoutParams.mIsCaptionVisible);
81     }
82 
83     @SuppressLint("MissingPermission")
relayout(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT, boolean isCaptionVisible)84     void relayout(ActivityManager.RunningTaskInfo taskInfo,
85             SurfaceControl.Transaction startT, SurfaceControl.Transaction finishT,
86             boolean isCaptionVisible) {
87         final WindowContainerTransaction wct = new WindowContainerTransaction();
88 
89         updateRelayoutParams(mRelayoutParams, taskInfo, isCaptionVisible);
90 
91         relayout(mRelayoutParams, startT, finishT, wct, mRootView, mResult);
92         // After this line, mTaskInfo is up-to-date and should be used instead of taskInfo
93         mBgExecutor.execute(() -> mTaskOrganizer.applyTransaction(wct));
94 
95         if (mResult.mRootView == null) {
96             // This means something blocks the window decor from showing, e.g. the task is hidden.
97             // Nothing is set up in this case including the decoration surface.
98             return;
99         }
100 
101         if (mRootView != mResult.mRootView) {
102             mRootView = mResult.mRootView;
103             setupRootView(mResult.mRootView, mClickListener);
104         }
105 
106         if (DesktopModeFlags.ENABLE_DESKTOP_APP_HANDLE_ANIMATION.isTrue()) {
107             setCaptionVisibility(mRootView, mRelayoutParams.mIsCaptionVisible);
108         }
109     }
110 
setCaptionVisibility(@onNull View rootView, boolean visible)111     private void setCaptionVisibility(@NonNull View rootView, boolean visible) {
112         final int v = visible ? View.VISIBLE : View.GONE;
113         final View captionView = rootView.findViewById(getCaptionViewId());
114         if (captionView != null) {
115             captionView.setVisibility(v);
116         }
117     }
118 
119     @Override
120     @NonNull
calculateValidDragArea()121     Rect calculateValidDragArea() {
122         return new Rect();
123     }
124 
125     @Override
getCaptionViewId()126     int getCaptionViewId() {
127         return R.id.caption;
128     }
129 
updateRelayoutParams( RelayoutParams relayoutParams, ActivityManager.RunningTaskInfo taskInfo, boolean isCaptionVisible)130     private void updateRelayoutParams(
131             RelayoutParams relayoutParams,
132             ActivityManager.RunningTaskInfo taskInfo,
133             boolean isCaptionVisible) {
134         relayoutParams.reset();
135         relayoutParams.mRunningTaskInfo = taskInfo;
136         // todo(b/382071404): update to car specific UI
137         relayoutParams.mLayoutResId = R.layout.caption_window_decor;
138         relayoutParams.mCaptionHeightId = R.dimen.freeform_decor_caption_height;
139         relayoutParams.mIsCaptionVisible =
140                 isCaptionVisible && mIsStatusBarVisible && !mIsKeyguardVisibleAndOccluded;
141         relayoutParams.mCaptionTopPadding = getTopPadding(taskInfo, relayoutParams);
142 
143         relayoutParams.mInsetSourceFlags |= FLAG_FORCE_CONSUMING_OPAQUE_CAPTION_BAR;
144         relayoutParams.mApplyStartTransactionOnDraw = true;
145     }
146 
getTopPadding(ActivityManager.RunningTaskInfo taskInfo, RelayoutParams relayoutParams)147     private int getTopPadding(ActivityManager.RunningTaskInfo taskInfo,
148             RelayoutParams relayoutParams) {
149         Rect taskBounds = taskInfo.getConfiguration().windowConfiguration.getBounds();
150         InsetsState insetsState = mDisplayController.getInsetsState(taskInfo.displayId);
151         if (insetsState == null) {
152             return relayoutParams.mCaptionTopPadding;
153         }
154         Insets systemDecor = insetsState.calculateInsets(taskBounds,
155                 WindowInsets.Type.systemBars() & ~WindowInsets.Type.captionBar(),
156                 false /* ignoreVisibility */);
157         return systemDecor.top;
158     }
159 
160     /**
161      * Sets up listeners when a new root view is created.
162      */
setupRootView(View rootView, View.OnClickListener onClickListener)163     private void setupRootView(View rootView, View.OnClickListener onClickListener) {
164         final View caption = rootView.findViewById(R.id.caption);
165         final View close = caption.findViewById(R.id.close_window);
166         if (close != null) {
167             close.setOnClickListener(onClickListener);
168         }
169         final View back = caption.findViewById(R.id.back_button);
170         if (back != null) {
171             back.setOnClickListener(onClickListener);
172         }
173     }
174 }
175