• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2023 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 android.car.app;
18 
19 import static com.android.car.internal.util.VersionUtils.assertPlatformVersionAtLeast;
20 
21 import android.annotation.MainThread;
22 import android.annotation.NonNull;
23 import android.annotation.Nullable;
24 import android.annotation.RequiresApi;
25 import android.annotation.RequiresPermission;
26 import android.app.ActivityManager;
27 import android.app.ActivityOptions;
28 import android.app.PendingIntent;
29 import android.car.Car;
30 import android.car.PlatformVersion;
31 import android.car.annotation.ApiRequirements;
32 import android.car.builtin.util.Slogf;
33 import android.car.builtin.view.SurfaceControlHelper;
34 import android.car.builtin.view.TouchableInsetsProvider;
35 import android.car.builtin.view.ViewHelper;
36 import android.content.Context;
37 import android.content.Intent;
38 import android.graphics.Rect;
39 import android.graphics.Region;
40 import android.os.Build;
41 import android.os.DeadObjectException;
42 import android.os.RemoteException;
43 import android.util.Log;
44 import android.view.SurfaceControl;
45 import android.view.SurfaceHolder;
46 import android.view.SurfaceView;
47 
48 /**
49  * A {@link SurfaceView} that can embed a Task inside of it. The task management is done remotely
50  * in a process that has registered a TaskOrganizer with the system server.
51  * Usually this process is the Car System UI.
52  *
53  * @hide
54  */
55 @RequiresApi(Build.VERSION_CODES.UPSIDE_DOWN_CAKE)
56 public abstract class RemoteCarTaskView extends SurfaceView {
57     private static final String TAG = RemoteCarTaskView.class.getSimpleName();
58 
59     private final TouchableInsetsProvider mTouchableInsetsProvider;
60     private final SurfaceCallbackHandler mSurfaceCallbackHandler = new SurfaceCallbackHandler();
61     private final Rect mTmpRect = new Rect();
62     private boolean mInitialized = false;
63     boolean mSurfaceCreated = false;
64     private Region mObscuredTouchRegion;
65     private ICarTaskViewHost mICarTaskViewHost;
66 
RemoteCarTaskView(Context context)67     RemoteCarTaskView(Context context) {
68         super(context);
69         assertPlatformVersionAtLeast(PlatformVersion.VERSION_CODES.UPSIDE_DOWN_CAKE_0);
70         mTouchableInsetsProvider = new TouchableInsetsProvider(this);
71         getHolder().addCallback(mSurfaceCallbackHandler);
72     }
73 
74     /** Brings the embedded task to the front. Does nothing if there is no task. */
75     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
76     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
77             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
78     @MainThread
showEmbeddedTask()79     public void showEmbeddedTask() {
80         try {
81             mICarTaskViewHost.showEmbeddedTask();
82         } catch (RemoteException e) {
83             Slogf.e(TAG, "exception in showEmbeddedTask", e);
84         }
85     }
86 
87     /**
88      * Updates the WM bounds for the underlying task as per the current view bounds. Does nothing
89      * if there is no task.
90      */
91     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
92     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
93             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
94     @MainThread
updateWindowBounds()95     public void updateWindowBounds() {
96         ViewHelper.getBoundsOnScreen(RemoteCarTaskView.this, mTmpRect);
97         try {
98             mICarTaskViewHost.setWindowBounds(mTmpRect);
99         } catch (RemoteException e) {
100             Slogf.e(TAG, "exception in setWindowBounds", e);
101         }
102     }
103 
104     /**
105      * Indicates a region of the view that is not touchable.
106      *
107      * @param obscuredRect the obscured region of the view.
108      */
109     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
110             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
111     @MainThread
setObscuredTouchRect(@onNull Rect obscuredRect)112     public void setObscuredTouchRect(@NonNull Rect obscuredRect) {
113         mObscuredTouchRegion = obscuredRect != null ? new Region(obscuredRect) : null;
114         mTouchableInsetsProvider.setObscuredTouchRegion(mObscuredTouchRegion);
115     }
116 
117     /**
118      * Indicates a region of the view that is not touchable.
119      *
120      * @param obscuredRegion the obscured region of the view.
121      */
122     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
123             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
124     @MainThread
setObscuredTouchRegion(@onNull Region obscuredRegion)125     public void setObscuredTouchRegion(@NonNull Region obscuredRegion) {
126         mObscuredTouchRegion = obscuredRegion;
127         mTouchableInsetsProvider.setObscuredTouchRegion(mObscuredTouchRegion);
128     }
129 
130     /**
131      * @return the {@link android.app.ActivityManager.RunningTaskInfo} of the task currently
132      * running in the TaskView.
133      */
134     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
135             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
136     @MainThread
getTaskInfo()137     @Nullable public abstract ActivityManager.RunningTaskInfo getTaskInfo();
138 
139     /**
140      * @return true, if the task view is initialized.
141      */
142     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
143             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
144     @MainThread
isInitialized()145     public boolean isInitialized() {
146         return mInitialized;
147     }
148 
149     /**
150      * Adds the given insets on the Task.
151      *
152      * The given frame for the insets type are applied to the underlying task right away.
153      * If a rectangle for an insets type was added previously, it will be replaced with the
154      * new value.
155      * If a rectangle for a insets type was already added, but is not specified currently in
156      * {@code insets}, it will remain applied to the task. Clients should explicitly call
157      * {@link #removeInsets(int, int)} to remove the rectangle for that insets type from
158      * the underlying task.
159      *
160      * @param index An owner might add multiple insets sources with the same type.
161      *              This identifies them.
162      * @param type  The insets type of the insets source.
163      * @param frame The rectangle area of the insets source.
164      */
165     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
166     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
167             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
168     @MainThread
addInsets(int index, int type, @NonNull Rect frame)169     public void addInsets(int index, int type, @NonNull Rect frame) {
170         try {
171             mICarTaskViewHost.addInsets(index, type, frame);
172         } catch (RemoteException e) {
173             Log.e(TAG, "exception in addInsets", e);
174         }
175     }
176 
177     /**
178      * Removes the given insets from the Task.
179      *
180      * Note: This will only remove the insets that were added using
181      * {@link #addInsets(int, int, Rect)}
182      *
183      * @param index An owner might add multiple insets sources with the same type.
184      *              This identifies them.
185      * @param type  The insets type of the insets source. This doesn't accept the composite types.
186      */
187     @RequiresPermission(Car.PERMISSION_REGISTER_CAR_SYSTEM_UI_PROXY)
188     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
189             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
removeInsets(int index, int type)190     public void removeInsets(int index, int type) {
191         try {
192             mICarTaskViewHost.removeInsets(index, type);
193         } catch (RemoteException e) {
194             Log.e(TAG, "exception in removeInsets", e);
195         }
196     }
197 
setRemoteHost(@onNull ICarTaskViewHost carTaskViewHost)198     void setRemoteHost(@NonNull ICarTaskViewHost carTaskViewHost) {
199         mICarTaskViewHost = carTaskViewHost;
200 
201         if (mSurfaceCreated) {
202             if (!mInitialized) {
203                 onInitialized();
204                 mInitialized = true;
205             }
206         }
207     }
208 
209     /**
210      * Starts the activity from the given {@code PendingIntent}
211      *
212      * @param pendingIntent Intent used to launch an activity.
213      * @param fillInIntent Additional Intent data, see {@link Intent#fillIn Intent.fillIn()}
214      * @param options options for the activity.
215      * @param launchBounds the bounds (window size and position) that the activity should be
216      *                      launched in, in pixels and in screen coordinates.
217      */
startActivity( @onNull PendingIntent pendingIntent, @Nullable Intent fillInIntent, @NonNull ActivityOptions options, @Nullable Rect launchBounds)218     void startActivity(
219             @NonNull PendingIntent pendingIntent,
220             @Nullable Intent fillInIntent,
221             @NonNull ActivityOptions options,
222             @Nullable Rect launchBounds) {
223         try {
224             mICarTaskViewHost.startActivity(
225                     pendingIntent, fillInIntent, options.toBundle(), launchBounds);
226         } catch (RemoteException exception) {
227             Slogf.e(TAG, "exception in startActivity", exception);
228         }
229     }
230 
createRootTask(int displayId)231     void createRootTask(int displayId) {
232         try {
233             mICarTaskViewHost.createRootTask(displayId);
234         } catch (RemoteException exception) {
235             Slogf.e(TAG, "exception in createRootTask", exception);
236         }
237     }
238 
createLaunchRootTask(int displayId, boolean embedHomeTask, boolean embedRecentsTask, boolean embedAssistantTask)239     void createLaunchRootTask(int displayId, boolean embedHomeTask, boolean embedRecentsTask,
240             boolean embedAssistantTask) {
241         try {
242             mICarTaskViewHost.createLaunchRootTask(displayId, embedHomeTask, embedRecentsTask,
243                     embedAssistantTask);
244         } catch (RemoteException exception) {
245             Slogf.e(TAG, "exception in createRootTask", exception);
246         }
247     }
248 
249     /** Release the resources associated with this task view. */
250     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
251             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
252     @MainThread
release()253     public void release() {
254         getHolder().removeCallback(mSurfaceCallbackHandler);
255         try {
256             mICarTaskViewHost.release();
257         } catch (DeadObjectException e) {
258             Slogf.w(TAG, "TaskView's host has already died", e);
259         } catch (RemoteException e) {
260             Slogf.e(TAG, "exception in release", e);
261         }
262         onReleased();
263     }
264 
265     /**
266      * Called when the task view is initialized. It is called only once for the lifetime of
267      * taskview.
268      */
onInitialized()269     abstract void onInitialized();
270 
271     /**
272      * Called when the task view is released. It is only called once for the lifetime of task view.
273      */
onReleased()274     abstract void onReleased();
275 
276     /**
277      * Called when the task has appeared in the taskview.
278      *
279      * @param taskInfo the taskInfo of the task that has appeared.
280      * @param leash the suface control for the task surface.
281      */
onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash)282     void onTaskAppeared(ActivityManager.RunningTaskInfo taskInfo, SurfaceControl leash) {
283     }
284 
285     /**
286      * Called when the task's info has changed.
287      *
288      * @param taskInfo the taskInfo of the task that has a change in info.
289      */
onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo)290     void onTaskInfoChanged(ActivityManager.RunningTaskInfo taskInfo) {
291     }
292 
293     /**
294      * Called when the task has vanished.
295      *
296      * @param taskInfo the taskInfo of the task that has vanished.
297      */
onTaskVanished(ActivityManager.RunningTaskInfo taskInfo)298     void onTaskVanished(ActivityManager.RunningTaskInfo taskInfo) {
299     }
300 
301     @Override
302     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
303             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
onAttachedToWindow()304     public void onAttachedToWindow() {
305         super.onAttachedToWindow();
306         mTouchableInsetsProvider.addToViewTreeObserver();
307     }
308 
309     @Override
310     @ApiRequirements(minCarVersion = ApiRequirements.CarVersion.UPSIDE_DOWN_CAKE_0,
311             minPlatformVersion = ApiRequirements.PlatformVersion.UPSIDE_DOWN_CAKE_0)
onDetachedFromWindow()312     public void onDetachedFromWindow() {
313         super.onDetachedFromWindow();
314         mTouchableInsetsProvider.removeFromViewTreeObserver();
315     }
316 
317     private class SurfaceCallbackHandler implements SurfaceHolder.Callback {
318         @Override
surfaceCreated(@onNull SurfaceHolder holder)319         public void surfaceCreated(@NonNull SurfaceHolder holder) {
320             if (mICarTaskViewHost != null) {
321                 if (!mInitialized) {
322                     onInitialized();
323                     mInitialized = true;
324                 }
325             }
326             mSurfaceCreated = true;
327             try {
328                 mICarTaskViewHost.notifySurfaceCreated(
329                         SurfaceControlHelper.copy(getSurfaceControl()));
330             } catch (RemoteException e) {
331                 Slogf.e(TAG, "exception in notifySurfaceCreated", e);
332             }
333         }
334 
335         @Override
surfaceChanged( @onNull SurfaceHolder holder, int format, int width, int height)336         public void surfaceChanged(
337                 @NonNull SurfaceHolder holder, int format, int width, int height) {
338             try {
339                 ViewHelper.getBoundsOnScreen(RemoteCarTaskView.this, mTmpRect);
340                 mICarTaskViewHost.setWindowBounds(mTmpRect);
341             } catch (RemoteException e) {
342                 Slogf.e(TAG, "exception in setWindowBounds", e);
343             }
344         }
345 
346         @Override
surfaceDestroyed(@onNull SurfaceHolder holder)347         public void surfaceDestroyed(@NonNull SurfaceHolder holder) {
348             mSurfaceCreated = false;
349             try {
350                 mICarTaskViewHost.notifySurfaceDestroyed();
351             } catch (RemoteException e) {
352                 Slogf.e(TAG, "exception in notifySurfaceDestroyed", e);
353             }
354         }
355     }
356 }
357