/* * 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. */ package android.car.view; import android.annotation.MainThread; import android.annotation.NonNull; import android.annotation.Nullable; import android.annotation.RequiresPermission; import android.annotation.SystemApi; import android.app.Activity; import android.car.Car; import android.car.app.CarActivityManager; import android.car.builtin.util.Slogf; import android.car.builtin.view.TouchableInsetsProvider; import android.content.Context; import android.graphics.Rect; import android.graphics.Region; import android.os.IBinder; import android.util.AttributeSet; import android.util.Dumpable; import android.util.Log; import android.util.Pair; import android.util.Slog; import android.view.Surface; import android.view.SurfaceControl; import android.view.SurfaceHolder; import android.view.SurfaceView; import com.android.internal.annotations.VisibleForTesting; import java.io.PrintWriter; /** * {@link SurfaceView} which can render the {@link Surface} of mirrored Task. * * @hide */ @SystemApi @SuppressWarnings("[NotCloseable]") // View object won't be used in try-with-resources statement. public final class MirroredSurfaceView extends SurfaceView { private static final String TAG = MirroredSurfaceView.class.getSimpleName(); private static final boolean DBG = Slogf.isLoggable(TAG, Log.DEBUG); private final SurfaceControl.Transaction mTransaction; private final TouchableInsetsProvider mTouchableInsetsProvider; private SurfaceControl mMirroredSurface; private Rect mSourceBounds; private CarActivityManager mCarAM; public MirroredSurfaceView(@NonNull Context context) { this(context, /* attrs= */ null); } public MirroredSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs) { this(context, attrs, /* defStyle= */ 0); } public MirroredSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) { this(context, attrs, defStyle, /* defStyleRes= */ 0); } public MirroredSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes) { this(context, attrs, defStyleAttr, defStyleRes, new SurfaceControl.Transaction(), /* touchableInsetsProvider= */ null); } @VisibleForTesting MirroredSurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr, int defStyleRes, SurfaceControl.Transaction transaction, TouchableInsetsProvider touchableInsetsProvider) { super(context, attrs, defStyleAttr, defStyleRes); mTransaction = transaction; mTouchableInsetsProvider = touchableInsetsProvider != null ? touchableInsetsProvider : new TouchableInsetsProvider(this); getHolder().addCallback(mSurfaceCallback); if (context instanceof Activity) { ((Activity) context).addDumpable(mDumper); } Car.createCar(/* context= */ context, /* handler= */ null, Car.CAR_WAIT_TIMEOUT_WAIT_FOREVER, (car, ready) -> { if (!ready) { Slog.w(TAG, "CarService looks crashed"); mCarAM = null; return; } mCarAM = car.getCarManager(CarActivityManager.class); }); } /** * Attaches the mirrored Surface which is represented by the given token to this View. *
* Note: MirroredSurfaceView will hold the Surface unless you call {@link #release()}
* explicitly. This is so that the host can keep the Surface when {@link Activity#onStop()} and
* {@link Activity#onStart()} are called again.
*
* @param token A token to access the Task Surface to mirror.
* @return true if the operation is successful.
*/
@RequiresPermission(Car.PERMISSION_ACCESS_MIRRORRED_SURFACE)
@MainThread
public boolean mirrorSurface(@NonNull IBinder token) {
if (mCarAM == null) {
Slogf.e(TAG, "Failed to mirrorSurface because CarService isn't ready yet");
return false;
}
if (mMirroredSurface != null) {
removeMirroredSurface();
}
Pair