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

Commit b1e3ff40 authored by android-build-team Robot's avatar android-build-team Robot
Browse files

Snap for 6236599 from b8960726 to qt-qpr3-release

Change-Id: I0a106ffc073d71654e20ba70f627a335072f0edd
parents 9e8d7845 b8960726
Loading
Loading
Loading
Loading
+15 −19
Original line number Diff line number Diff line
@@ -41,7 +41,6 @@ import android.os.Build;
import android.os.Handler;
import android.os.IBinder;
import android.os.Looper;
import android.os.RemoteException;
import android.util.Log;
import android.view.Display;
import android.view.GestureDetector;
@@ -654,20 +653,22 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt
                    }
                });

        if (mCarNotificationListener != null) {
            try {
                // If we already had a notification listener we need to unreigster is before
                // making a new one
                mCarNotificationListener.unregisterAsSystemService();
            } catch (RemoteException e) {
                Log.e(TAG, "Error unregistering notification listener.");
            }
        }

        mCarNotificationListener = new CarNotificationListener();
        mCarUxRestrictionManagerWrapper = new CarUxRestrictionManagerWrapper();

        if (mCarNotificationListener == null) {
            // Only make and register a listener if we don't already have one since
            // #connectNotificationsUI can be called multiple times on locale change.
            mCarNotificationListener = new CarNotificationListener();
            mNotificationDataManager = new NotificationDataManager();

            CarHeadsUpNotificationManager carHeadsUpNotificationManager =
                    new CarSystemUIHeadsUpNotificationManager(mContext,
                            mNotificationClickHandlerFactory, mNotificationDataManager);
            mCarNotificationListener.registerAsSystemService(mContext,
                    mCarUxRestrictionManagerWrapper,
                    carHeadsUpNotificationManager, mNotificationDataManager);
        }

        mNotificationDataManager.setOnUnseenCountUpdateListener(() -> {
            if (mNavigationBarView != null && mNotificationDataManager != null) {
                onUseenCountUpdate(mNotificationDataManager.getUnseenNotificationCount());
@@ -676,13 +677,8 @@ public class CarStatusBar extends StatusBar implements CarBatteryController.Batt

        mEnableHeadsUpNotificationWhenNotificationShadeOpen = mContext.getResources().getBoolean(
                R.bool.config_enableHeadsUpNotificationWhenNotificationShadeOpen);
        CarHeadsUpNotificationManager carHeadsUpNotificationManager =
                new CarSystemUIHeadsUpNotificationManager(mContext,
                        mNotificationClickHandlerFactory, mNotificationDataManager);
        mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);

        mCarNotificationListener.registerAsSystemService(mContext, mCarUxRestrictionManagerWrapper,
                carHeadsUpNotificationManager, mNotificationDataManager);
        mNotificationClickHandlerFactory.setNotificationDataManager(mNotificationDataManager);

        mNotificationView = mStatusBarWindow.findViewById(R.id.notification_view);
        View glassPane = mStatusBarWindow.findViewById(R.id.glass_pane);
+17 −0
Original line number Diff line number Diff line
@@ -491,4 +491,21 @@
    <!-- Respect the drawable/rounded.xml that allow to customize as multiple radius corner path -->
    <bool name="config_roundedCornerMultipleRadius">false</bool>

    <!-- 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
+79 −4
Original line number Diff line number Diff line
@@ -29,6 +29,7 @@ import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.ObjectAnimator;
import android.annotation.Dimension;
import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.Fragment;
import android.content.BroadcastReceiver;
@@ -37,6 +38,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;
@@ -110,6 +112,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,

    private DisplayManager mDisplayManager;
    private DisplayManager.DisplayListener mDisplayListener;
    private CameraAvailabilityListener mCameraListener;

    @VisibleForTesting
    protected int mRoundedDefault;
@@ -133,6 +136,25 @@ public class ScreenDecorations extends SystemUI implements Tunable,
    private boolean mInGesturalMode;
    private boolean mIsRoundedCornerMultipleRadius;

    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
            mCutoutTop.setProtection(protectionPath, bounds);
            mCutoutTop.setShowProtection(true);
            mCutoutBottom.setProtection(protectionPath, bounds);
            mCutoutBottom.setShowProtection(true);
        }

        @Override
        public void onHideCameraProtection() {
            // Go back to the regular anti-aliasing
            mCutoutTop.setShowProtection(false);
            mCutoutBottom.setShowProtection(false);
        }
    };

    /**
     * Converts a set of {@link Rect}s into a {@link Region}
     *
@@ -331,6 +353,7 @@ public class ScreenDecorations extends SystemUI implements Tunable,
        updateRoundedCornerRadii();
        if (hasRoundedCorners() || shouldDrawCutout() || shouldHostHandles()) {
            setupDecorations();
            setupCameraListener();
        }

        mDisplayListener = new DisplayManager.DisplayListener() {
@@ -446,6 +469,16 @@ public class ScreenDecorations extends SystemUI implements Tunable,
                new ValidatingPreDrawListener(mBottomOverlay));
    }

    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) {
@@ -841,6 +874,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 boolean mInitialStart;
        private final Runnable mVisibilityChangedListener;
@@ -887,7 +927,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);
@@ -915,6 +961,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 boolean isStart() {
            final boolean flipped = (mRotation == RotationUtils.ROTATION_SEASCAPE
                    || mRotation == RotationUtils.ROTATION_UPSIDE_DOWN);
@@ -961,6 +1023,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,
@@ -1018,10 +1083,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) {