• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 
17 package com.android.systemui.car.cluster;
18 
19 import static android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_BOUNDS;
20 import static android.car.cluster.ClusterHomeManager.CONFIG_DISPLAY_ID;
21 
22 import android.car.Car;
23 import android.car.cluster.ClusterHomeManager;
24 import android.car.cluster.ClusterState;
25 import android.graphics.Rect;
26 import android.util.Slog;
27 import android.view.Display;
28 import android.view.SurfaceControl;
29 import android.window.DisplayAreaInfo;
30 import android.window.WindowContainerToken;
31 import android.window.WindowContainerTransaction;
32 
33 import com.android.systemui.CoreStartable;
34 import com.android.systemui.car.CarServiceProvider;
35 import com.android.systemui.dagger.SysUISingleton;
36 import com.android.systemui.dagger.qualifiers.Main;
37 import com.android.wm.shell.RootTaskDisplayAreaOrganizer;
38 
39 import java.util.Optional;
40 import java.util.concurrent.Executor;
41 
42 import javax.inject.Inject;
43 
44 /***
45  * Controls the RootTDA of cluster display per CLUSTER_DISPLAY_STATE message.
46  */
47 @SysUISingleton
48 public class ClusterDisplayController implements CoreStartable {
49     private static final String TAG = ClusterDisplayController.class.getSimpleName();
50     private static final boolean DBG = false;
51 
52     private final RootTaskDisplayAreaOrganizer mRootTDAOrganizer;
53     private final CarServiceProvider mCarServiceProvider;
54     private final Executor mMainExecutor;
55 
56     private ClusterHomeManager mClusterHomeManager;
57     private WindowContainerToken mRootTDAToken;
58     private ClusterState mClusterState;
59 
60     @Inject
ClusterDisplayController( Optional<RootTaskDisplayAreaOrganizer> rootTDAOrganizer, CarServiceProvider carServiceProvider, @Main Executor mainExecutor)61     public ClusterDisplayController(
62             Optional<RootTaskDisplayAreaOrganizer> rootTDAOrganizer,
63             CarServiceProvider carServiceProvider, @Main Executor mainExecutor) {
64         mRootTDAOrganizer = rootTDAOrganizer.orElse(null);
65         mCarServiceProvider = carServiceProvider;
66         mMainExecutor = mainExecutor;
67     }
68 
69     @Override
start()70     public void start() {
71         if (mRootTDAOrganizer == null) {
72             Slog.w(TAG, "ClusterDisplayController is disabled because of no "
73                     + "RootTaskDisplayAreaOrganizer");
74             return;
75         }
76         mCarServiceProvider.addListener(mCarServiceOnConnectedListener);
77     }
78 
79     private final CarServiceProvider.CarServiceOnConnectedListener mCarServiceOnConnectedListener =
80             new CarServiceProvider.CarServiceOnConnectedListener() {
81         @Override
82         public void onConnected(Car car) {
83             mClusterHomeManager = (ClusterHomeManager) car.getCarManager(Car.CLUSTER_HOME_SERVICE);
84             if (mClusterHomeManager == null) {
85                 Slog.w(TAG, "ClusterHomeManager is disabled");
86                 return;
87             }
88             mClusterHomeManager.registerClusterStateListener(mMainExecutor, mClusterStateListener);
89 
90             mClusterState = mClusterHomeManager.getClusterState();
91             if (mClusterState.displayId != Display.INVALID_DISPLAY) {
92                 mRootTDAOrganizer.registerListener(mClusterState.displayId, mRootTDAListener);
93             }
94         }
95     };
96 
97     private final ClusterHomeManager.ClusterStateListener mClusterStateListener =
98             new ClusterHomeManager.ClusterStateListener() {
99         @Override
100         public void onClusterStateChanged(ClusterState state, int changes) {
101             // Need to update mClusterState first, since mClusterState will be referenced during
102             // registerListener() -> onDisplayAreaAppeared() -> resizeTDA().
103             mClusterState = state;
104             if (DBG) {
105                 Slog.d(TAG, "onClusterStateChanged: changes=" + changes
106                         + ", displayId=" + state.displayId);
107             }
108             if ((changes & CONFIG_DISPLAY_ID) != 0) {
109                 if (state.displayId != Display.INVALID_DISPLAY) {
110                     mRootTDAOrganizer.registerListener(state.displayId, mRootTDAListener);
111                 } else {
112                     mRootTDAOrganizer.unregisterListener(mRootTDAListener);
113                 }
114             }
115             if ((changes & CONFIG_DISPLAY_BOUNDS) != 0 && mRootTDAToken != null) {
116                 resizeTDA(mRootTDAToken, state.bounds, state.displayId);
117             }
118         }
119     };
120 
121     private final RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener mRootTDAListener =
122             new RootTaskDisplayAreaOrganizer.RootTaskDisplayAreaListener() {
123         @Override
124         public void onDisplayAreaAppeared(DisplayAreaInfo displayAreaInfo) {
125             if (DBG) Slog.d(TAG, "onDisplayAreaAppeared: " + displayAreaInfo);
126             if (mClusterState != null && mClusterState.displayId != Display.INVALID_DISPLAY) {
127                 resizeTDA(displayAreaInfo.token, mClusterState.bounds, mClusterState.displayId);
128             }
129             mRootTDAToken = displayAreaInfo.token;
130         }
131 
132         @Override
133         public void onDisplayAreaVanished(DisplayAreaInfo displayAreaInfo) {
134             if (DBG) Slog.d(TAG, "onDisplayAreaVanished: " + displayAreaInfo);
135             mRootTDAToken = null;
136         }
137 
138         @Override
139         public void onDisplayAreaInfoChanged(DisplayAreaInfo displayAreaInfo) {
140             if (DBG) Slog.d(TAG, "onDisplayAreaInfoChanged: " + displayAreaInfo);
141             mRootTDAToken = displayAreaInfo.token;
142         }
143     };
144 
resizeTDA(WindowContainerToken token, Rect bounds, int displayId)145     private void resizeTDA(WindowContainerToken token, Rect bounds, int displayId) {
146         if (DBG) {
147             Slog.d(TAG, "resizeTDA: token=" + token + ", bounds=" + bounds
148                     + ", displayId=" + displayId);
149         }
150         WindowContainerTransaction wct = new WindowContainerTransaction();
151         wct.setBounds(token, bounds);
152         wct.setAppBounds(token, bounds);
153         mRootTDAOrganizer.applyTransaction(wct);
154 
155         SurfaceControl.Transaction tx = new SurfaceControl.Transaction();
156         mRootTDAOrganizer.setPosition(tx, displayId, bounds.left, bounds.top);
157         tx.apply();
158     }
159 }
160