Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e1ab8fe3 authored by Chris Göllner's avatar Chris Göllner Committed by Android (Google) Code Review
Browse files

Merge "[Status Bar][Camera cutout protection] Add support for multiple displays" into main

parents 4ba29f49 50844eec
Loading
Loading
Loading
Loading
+4 −0
Original line number Diff line number Diff line
@@ -543,12 +543,16 @@

    <!--  ID for the camera of outer display that needs extra protection -->
    <string translatable="false" name="config_protectedCameraId"></string>
    <!--  Physical ID for the camera of outer display that needs extra protection -->
    <string translatable="false" name="config_protectedPhysicalCameraId"></string>

    <!-- Similar to config_frontBuiltInDisplayCutoutProtection but for inner display. -->
    <string translatable="false" name="config_innerBuiltInDisplayCutoutProtection"></string>

    <!-- ID for the camera of inner display that needs extra protection -->
    <string translatable="false" name="config_protectedInnerCameraId"></string>
    <!-- Physical ID for the camera of inner display that needs extra protection -->
    <string translatable="false" name="config_protectedInnerPhysicalCameraId"></string>

    <!-- Comma-separated list of packages to exclude from camera protection e.g.
    "com.android.systemui,com.android.xyz" -->
+98 −34
Original line number Diff line number Diff line
@@ -38,23 +38,72 @@ class CameraAvailabilityListener(
    excludedPackages: String,
    private val executor: Executor
) {
    private var activeProtectionInfo: CameraProtectionInfo? = null
    private var openCamera: OpenCameraInfo? = null
    private val unavailablePhysicalCameras = mutableSetOf<String>()
    private val excludedPackageIds: Set<String>
    private val listeners = mutableListOf<CameraTransitionCallback>()
    private val availabilityCallback: CameraManager.AvailabilityCallback =
        object : CameraManager.AvailabilityCallback() {
                override fun onCameraClosed(cameraId: String) {
                    cameraProtectionInfoList.forEach {
                        if (cameraId == it.cameraId) {
            override fun onCameraClosed(logicalCameraId: String) {
                openCamera = null
                if (activeProtectionInfo?.logicalCameraId == logicalCameraId) {
                    notifyCameraInactive()
                }
                activeProtectionInfo = null
            }

            override fun onCameraOpened(logicalCameraId: String, packageId: String) {
                openCamera = OpenCameraInfo(logicalCameraId, packageId)
                if (isExcluded(packageId)) {
                    return
                }
                val protectionInfo =
                    cameraProtectionInfoList.firstOrNull {
                        logicalCameraId == it.logicalCameraId &&
                            it.physicalCameraId !in unavailablePhysicalCameras
                    }
                if (protectionInfo != null) {
                    activeProtectionInfo = protectionInfo
                    notifyCameraActive(protectionInfo)
                }
            }

                override fun onCameraOpened(cameraId: String, packageId: String) {
                    cameraProtectionInfoList.forEach {
                        if (cameraId == it.cameraId && !isExcluded(packageId)) {
                            notifyCameraActive(it)
            override fun onPhysicalCameraAvailable(
                logicalCameraId: String,
                physicalCameraId: String
            ) {
                unavailablePhysicalCameras -= physicalCameraId
                val openCamera = this@CameraAvailabilityListener.openCamera ?: return
                if (openCamera.logicalCameraId != logicalCameraId) {
                    return
                }
                if (isExcluded(openCamera.packageId)) {
                    return
                }
                val newActiveInfo =
                    cameraProtectionInfoList.find {
                        it.logicalCameraId == logicalCameraId &&
                            it.physicalCameraId == physicalCameraId
                    }
                if (newActiveInfo != null) {
                    activeProtectionInfo = newActiveInfo
                    notifyCameraActive(newActiveInfo)
                }
            }

            override fun onPhysicalCameraUnavailable(
                logicalCameraId: String,
                physicalCameraId: String
            ) {
                unavailablePhysicalCameras += physicalCameraId
                val activeInfo = activeProtectionInfo ?: return
                if (
                    activeInfo.logicalCameraId == logicalCameraId &&
                        activeInfo.physicalCameraId == physicalCameraId
                ) {
                    activeProtectionInfo = null
                    notifyCameraInactive()
                }
            }
        }
@@ -106,24 +155,21 @@ class CameraAvailabilityListener(
        listeners.forEach { it.onHideCameraProtection() }
    }

    /**
     * Callbacks to tell a listener that a relevant camera turned on and off.
     */
    /** Callbacks to tell a listener that a relevant camera turned on and off. */
    interface CameraTransitionCallback {
        fun onApplyCameraProtection(protectionPath: Path, bounds: Rect)

        fun onHideCameraProtection()
    }

    companion object Factory {
        fun build(context: Context, executor: Executor): CameraAvailabilityListener {
            val manager = context
                    .getSystemService(Context.CAMERA_SERVICE) as CameraManager
            val manager = context.getSystemService(Context.CAMERA_SERVICE) as CameraManager
            val res = context.resources
            val cameraProtectionInfoList = loadCameraProtectionInfoList(res)
            val excluded = res.getString(R.string.config_cameraProtectionExcludedPackages)

            return CameraAvailabilityListener(
                    manager, cameraProtectionInfoList, excluded, executor)
            return CameraAvailabilityListener(manager, cameraProtectionInfoList, excluded, executor)
        }

        private fun pathFromString(pathString: String): Path {
@@ -140,17 +186,21 @@ class CameraAvailabilityListener(

        private fun loadCameraProtectionInfoList(res: Resources): List<CameraProtectionInfo> {
            val list = mutableListOf<CameraProtectionInfo>()
            val front = loadCameraProtectionInfo(
            val front =
                loadCameraProtectionInfo(
                    res,
                    R.string.config_protectedCameraId,
                    R.string.config_protectedPhysicalCameraId,
                    R.string.config_frontBuiltInDisplayCutoutProtection
                )
            if (front != null) {
                list.add(front)
            }
            val inner = loadCameraProtectionInfo(
            val inner =
                loadCameraProtectionInfo(
                    res,
                    R.string.config_protectedInnerCameraId,
                    R.string.config_protectedInnerPhysicalCameraId,
                    R.string.config_innerBuiltInDisplayCutoutProtection
                )
            if (inner != null) {
@@ -162,28 +212,42 @@ class CameraAvailabilityListener(
        private fun loadCameraProtectionInfo(
            res: Resources,
            cameraIdRes: Int,
            physicalCameraIdRes: Int,
            pathRes: Int
        ): CameraProtectionInfo? {
            val cameraId = res.getString(cameraIdRes)
            if (cameraId == null || cameraId.isEmpty()) {
            val logicalCameraId = res.getString(cameraIdRes)
            if (logicalCameraId.isNullOrEmpty()) {
                return null
            }
            val physicalCameraId = res.getString(physicalCameraIdRes)
            val protectionPath = pathFromString(res.getString(pathRes))
            val computed = RectF()
            protectionPath.computeBounds(computed)
            val protectionBounds = Rect(
            val protectionBounds =
                Rect(
                    computed.left.roundToInt(),
                    computed.top.roundToInt(),
                    computed.right.roundToInt(),
                    computed.bottom.roundToInt()
                )
            return CameraProtectionInfo(cameraId, protectionPath, protectionBounds)
            return CameraProtectionInfo(
                logicalCameraId,
                physicalCameraId,
                protectionPath,
                protectionBounds
            )
        }
    }

    data class CameraProtectionInfo(
            val cameraId: String,
        val logicalCameraId: String,
        val physicalCameraId: String?,
        val cutoutProtectionPath: Path,
            val cutoutBounds: Rect
        val cutoutBounds: Rect,
    )

    private data class OpenCameraInfo(
        val logicalCameraId: String,
        val packageId: String,
    )
}
+15 −0
Original line number Diff line number Diff line
@@ -179,6 +179,7 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
    @VisibleForTesting
    protected DisplayInfo mDisplayInfo = new DisplayInfo();
    private DisplayCutout mDisplayCutout;
    private boolean mPendingManualConfigUpdate;

    @VisibleForTesting
    protected void showCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
@@ -571,6 +572,11 @@ public class ScreenDecorations implements CoreStartable, Dumpable {
                        setupDecorations();
                        return;
                    }

                    if (mPendingManualConfigUpdate) {
                        mPendingManualConfigUpdate = false;
                        onConfigurationChanged(mContext.getResources().getConfiguration());
                    }
                }
            }
        };
@@ -1064,6 +1070,15 @@ public class ScreenDecorations implements CoreStartable, Dumpable {

        mExecutor.execute(() -> {
            Trace.beginSection("ScreenDecorations#onConfigurationChanged");
            mContext.getDisplay().getDisplayInfo(mDisplayInfo);
            if (displaySizeChanged(mDisplaySize, mDisplayInfo)) {
                // We expect the display change event to happen first, but in this case, we received
                // onConfigurationChanged first.
                // Return so that we handle the display change event first, and then manually
                // trigger the config update.
                mPendingManualConfigUpdate = true;
                return;
            }
            int oldRotation = mRotation;
            mPendingConfigChange = false;
            updateConfiguration();
+297 −88

File changed.

Preview size limit exceeded, changes collapsed.