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

Commit 99608f31 authored by TreeHugger Robot's avatar TreeHugger Robot Committed by Android (Google) Code Review
Browse files

Merge "Add SystemUI support for front-facing camera protection" into rvc-dev

parents fff9a304 1698145b
Loading
Loading
Loading
Loading
+17 −0
Original line number Diff line number Diff line
@@ -506,4 +506,21 @@
        <item>@*android:string/status_bar_headset</item>
    </string-array>

    <!-- A path similar to frameworks/base/core/res/res/values/config.xml
      config_mainBuiltInDisplayCutout that describes a path larger than the exact path of a display
      cutout. If present as well as config_enableDisplayCutoutProtection is set to true, then
      SystemUI will draw this "protection path" instead of the display cutout path that is normally
      used for anti-aliasing.

      This path will only be drawn when the front-facing camera turns on, otherwise the main
      DisplayCutout path will be rendered
       -->
    <string translatable="false" name="config_frontBuiltInDisplayCutoutProtection"></string>

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

    <!--  Flag to turn on the rendering of the above path or not  -->
    <bool name="config_enableDisplayCutoutProtection">false</bool>

</resources>
+138 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 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 com.android.systemui

import android.content.Context
import android.graphics.Path
import android.graphics.Rect
import android.graphics.RectF
import android.hardware.camera2.CameraManager
import android.util.PathParser
import java.util.concurrent.Executor

import kotlin.math.roundToInt

const val TAG = "CameraOpTransitionController"

/**
 * Listens for usage of the Camera and controls the ScreenDecorations transition to show extra
 * protection around a display cutout based on config_frontBuiltInDisplayCutoutProtection and
 * config_enableDisplayCutoutProtection
 */
class CameraAvailabilityListener(
    private val cameraManager: CameraManager,
    private val cutoutProtectionPath: Path,
    private val targetCameraId: String,
    private val executor: Executor
) {
    private var cutoutBounds = Rect()
    private val listeners = mutableListOf<CameraTransitionCallback>()
    private val availabilityCallback: CameraManager.AvailabilityCallback =
            object : CameraManager.AvailabilityCallback() {
                override fun onCameraAvailable(cameraId: String) {
                    if (targetCameraId == cameraId) {
                        notifyCameraInactive()
                    }
                }

                override fun onCameraUnavailable(cameraId: String) {
                    if (targetCameraId == cameraId) {
                        notifyCameraActive()
                    }
                }
    }

    init {
        val computed = RectF()
        cutoutProtectionPath.computeBounds(computed, false /* unused */)
        cutoutBounds.set(
                computed.left.roundToInt(),
                computed.top.roundToInt(),
                computed.right.roundToInt(),
                computed.bottom.roundToInt())
    }

    /**
     * Start listening for availability events, and maybe notify listeners
     *
     * @return true if we started listening
     */
    fun startListening() {
        registerCameraListener()
    }

    fun stop() {
        unregisterCameraListener()
    }

    fun addTransitionCallback(callback: CameraTransitionCallback) {
        listeners.add(callback)
    }

    fun removeTransitionCallback(callback: CameraTransitionCallback) {
        listeners.remove(callback)
    }

    private fun registerCameraListener() {
        cameraManager.registerAvailabilityCallback(executor, availabilityCallback)
    }

    private fun unregisterCameraListener() {
        cameraManager.unregisterAvailabilityCallback(availabilityCallback)
    }

    private fun notifyCameraActive() {
        listeners.forEach { it.onApplyCameraProtection(cutoutProtectionPath, cutoutBounds) }
    }

    private fun notifyCameraInactive() {
        listeners.forEach { it.onHideCameraProtection() }
    }

    /**
     * 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 res = context.resources
            val pathString = res.getString(R.string.config_frontBuiltInDisplayCutoutProtection)
            val cameraId = res.getString(R.string.config_protectedCameraId)

            return CameraAvailabilityListener(
                    manager, pathFromString(pathString), cameraId, executor)
        }

        private fun pathFromString(pathString: String): Path {
            val spec = pathString.trim()
            val p: Path
            try {
                p = PathParser.createPathFromPathData(spec)
            } catch (e: Throwable) {
                throw IllegalArgumentException("Invalid protection path", e)
            }

            return p
        }
    }
}
 No newline at end of file
+81 −4
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import android.annotation.Dimension;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.content.BroadcastReceiver;
import android.content.Context;
@@ -36,6 +37,7 @@ import android.content.Intent;
import android.content.IntentFilter;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Matrix;
@@ -105,6 +107,7 @@ public class ScreenDecorations extends SystemUI implements Tunable {
    private final Handler mMainHandler;
    private final TunerService mTunerService;
    private DisplayManager.DisplayListener mDisplayListener;
    private CameraAvailabilityListener mCameraListener;

    @VisibleForTesting
    protected int mRoundedDefault;
@@ -122,6 +125,26 @@ public class ScreenDecorations extends SystemUI implements Tunable {
    private boolean mPendingRotationChange;
    private Handler mHandler;

    private CameraAvailabilityListener.CameraTransitionCallback mCameraTransitionCallback =
            new CameraAvailabilityListener.CameraTransitionCallback() {
        @Override
        public void onApplyCameraProtection(@NonNull Path protectionPath, @NonNull Rect bounds) {
            // Show the extra protection around the front facing camera if necessary
            for (DisplayCutoutView dcv : mCutoutViews) {
                dcv.setProtection(protectionPath, bounds);
                dcv.setShowProtection(true);
            }
        }

        @Override
        public void onHideCameraProtection() {
            // Go back to the regular anti-aliasing
            for (DisplayCutoutView dcv : mCutoutViews) {
                dcv.setShowProtection(false);
            }
        }
    };

    /**
     * Converts a set of {@link Rect}s into a {@link Region}
     *
@@ -169,6 +192,8 @@ public class ScreenDecorations extends SystemUI implements Tunable {
        mDisplayManager = mContext.getSystemService(DisplayManager.class);
        updateRoundedCornerRadii();
        setupDecorations();
        setupCameraListener();

        mDisplayListener = new DisplayManager.DisplayListener() {
            @Override
            public void onDisplayAdded(int displayId) {
@@ -443,6 +468,16 @@ public class ScreenDecorations extends SystemUI implements Tunable {
                : pos - rotation;
    }

    private void setupCameraListener() {
        Resources res = mContext.getResources();
        boolean enabled = res.getBoolean(R.bool.config_enableDisplayCutoutProtection);
        if (enabled) {
            mCameraListener = CameraAvailabilityListener.Factory.build(mContext, mHandler::post);
            mCameraListener.addTransitionCallback(mCameraTransitionCallback);
            mCameraListener.startListening();
        }
    }

    private final BroadcastReceiver mIntentReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
@@ -684,6 +719,13 @@ public class ScreenDecorations extends SystemUI implements Tunable {
        private final List<Rect> mBounds = new ArrayList();
        private final Rect mBoundingRect = new Rect();
        private final Path mBoundingPath = new Path();
        // Don't initialize these because they are cached elsewhere and may not exist
        private Rect mProtectionRect;
        private Path mProtectionPath;
        private Rect mTotalBounds = new Rect();
        // Whether or not to show the cutout protection path
        private boolean mShowProtection = false;

        private final int[] mLocation = new int[2];
        private final ScreenDecorations mDecorations;
        private int mColor = Color.BLACK;
@@ -727,7 +769,13 @@ public class ScreenDecorations extends SystemUI implements Tunable {
            super.onDraw(canvas);
            getLocationOnScreen(mLocation);
            canvas.translate(-mLocation[0], -mLocation[1]);
            if (!mBoundingPath.isEmpty()) {

            if (mShowProtection && !mProtectionRect.isEmpty()) {
                mPaint.setColor(mColor);
                mPaint.setStyle(Paint.Style.FILL);
                mPaint.setAntiAlias(true);
                canvas.drawPath(mProtectionPath, mPaint);
            } else if (!mBoundingPath.isEmpty()) {
                mPaint.setColor(mColor);
                mPaint.setStyle(Paint.Style.FILL);
                mPaint.setAntiAlias(true);
@@ -755,6 +803,22 @@ public class ScreenDecorations extends SystemUI implements Tunable {
            update();
        }

        void setProtection(Path protectionPath, Rect pathBounds) {
            mProtectionPath = protectionPath;
            mProtectionRect = pathBounds;
        }

        void setShowProtection(boolean shouldShow) {
            if (mShowProtection == shouldShow) {
                return;
            }

            mShowProtection = shouldShow;
            updateBoundingPath();
            requestLayout();
            invalidate();
        }

        private void update() {
            if (!isAttachedToWindow() || mDecorations.mPendingRotationChange) {
                return;
@@ -794,6 +858,9 @@ public class ScreenDecorations extends SystemUI implements Tunable {
            Matrix m = new Matrix();
            transformPhysicalToLogicalCoordinates(mInfo.rotation, dw, dh, m);
            mBoundingPath.transform(m);
            if (mProtectionPath != null) {
                mProtectionPath.transform(m);
            }
        }

        private static void transformPhysicalToLogicalCoordinates(@Surface.Rotation int rotation,
@@ -855,10 +922,20 @@ public class ScreenDecorations extends SystemUI implements Tunable {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                return;
            }

            if (mShowProtection) {
                // Make sure that our measured height encompases the protection
                mTotalBounds.union(mBoundingRect);
                mTotalBounds.union(mProtectionRect);
                setMeasuredDimension(
                        resolveSizeAndState(mTotalBounds.width(), widthMeasureSpec, 0),
                        resolveSizeAndState(mTotalBounds.height(), heightMeasureSpec, 0));
            } else {
                setMeasuredDimension(
                        resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
                        resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
            }
        }

        public static void boundsFromDirection(DisplayCutout displayCutout, int gravity,
                Rect out) {