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

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

Merge "Merge emulated cutout into either rounded corner overlay"

parents ce9bcc49 5b518854
Loading
Loading
Loading
Loading
+2 −2
Original line number Diff line number Diff line
@@ -22,7 +22,7 @@
        android:id="@+id/left"
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:layout_gravity="left"
        android:layout_gravity="left|top"
        android:tint="#ff000000"
        android:src="@drawable/rounded" />
    <ImageView
@@ -30,6 +30,6 @@
        android:layout_width="12dp"
        android:layout_height="12dp"
        android:tint="#ff000000"
        android:layout_gravity="right"
        android:layout_gravity="right|bottom"
        android:src="@drawable/rounded" />
</FrameLayout>
+1 −2
Original line number Diff line number Diff line
@@ -348,8 +348,7 @@
        <item>com.android.systemui.util.leak.GarbageMonitor$Service</item>
        <item>com.android.systemui.LatencyTester</item>
        <item>com.android.systemui.globalactions.GlobalActionsComponent</item>
        <item>com.android.systemui.RoundedCorners</item>
        <item>com.android.systemui.EmulatedDisplayCutout</item>
        <item>com.android.systemui.ScreenDecorations</item>
        <item>com.android.systemui.fingerprint.FingerprintDialogImpl</item>
    </string-array>

+2 −0
Original line number Diff line number Diff line
@@ -96,5 +96,7 @@

    <!-- For StatusBarIconContainer to tag its icon views -->
    <item type="id" name="status_bar_view_state_tag" />

    <item type="id" name="display_cutout" />
</resources>
+0 −129
Original line number Diff line number Diff line
/*
 * Copyright (C) 2017 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 static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.view.DisplayCutout;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowInsets;
import android.view.WindowManager;

import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;

/**
 * Emulates a display cutout by drawing its shape in an overlay as supplied by
 * {@link DisplayCutout}.
 */
public class EmulatedDisplayCutout extends SystemUI implements ConfigurationListener {
    private View mOverlay;
    private boolean mAttached;
    private WindowManager mWindowManager;

    @Override
    public void start() {
        Dependency.get(ConfigurationController.class).addCallback(this);

        mWindowManager = mContext.getSystemService(WindowManager.class);
        updateAttached();
    }

    @Override
    public void onOverlayChanged() {
        updateAttached();
    }

    private void updateAttached() {
        boolean shouldAttach = mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
        setAttached(shouldAttach);
    }

    private void setAttached(boolean attached) {
        if (attached && !mAttached) {
            if (mOverlay == null) {
                mOverlay = new CutoutView(mContext);
                mOverlay.setLayoutParams(getLayoutParams());
            }
            mWindowManager.addView(mOverlay, mOverlay.getLayoutParams());
            mAttached = true;
        } else if (!attached && mAttached) {
            mWindowManager.removeView(mOverlay);
            mAttached = false;
        }
    }

    private WindowManager.LayoutParams getLayoutParams() {
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                LayoutParams.MATCH_PARENT,
                WindowManager.LayoutParams.TYPE_NAVIGATION_BAR_PANEL,
                WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
                        | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
                        | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL
                        | WindowManager.LayoutParams.FLAG_SPLIT_TOUCH
                        | WindowManager.LayoutParams.FLAG_SLIPPERY
                        | WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN
                        | WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR,
                PixelFormat.TRANSLUCENT);
        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        lp.setTitle("EmulatedDisplayCutout");
        lp.gravity = Gravity.TOP;
        return lp;
    }

    private static class CutoutView extends View {
        private final Paint mPaint = new Paint();
        private final Path mBounds = new Path();

        CutoutView(Context context) {
            super(context);
        }

        @Override
        public WindowInsets onApplyWindowInsets(WindowInsets insets) {
            mBounds.reset();
            if (insets.getDisplayCutout() != null) {
                insets.getDisplayCutout().getBounds().getBoundaryPath(mBounds);
            }
            invalidate();
            return insets.consumeDisplayCutout();
        }

        @Override
        protected void onDraw(Canvas canvas) {
            if (!mBounds.isEmpty()) {
                mPaint.setColor(Color.BLACK);
                mPaint.setStyle(Paint.Style.FILL);

                canvas.drawPath(mBounds, mPaint);
            }
        }
    }
}
+415 −0
Original line number Diff line number Diff line
@@ -14,18 +14,29 @@

package com.android.systemui;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;

import static com.android.systemui.tuner.TunablePadding.FLAG_START;
import static com.android.systemui.tuner.TunablePadding.FLAG_END;

import android.app.Fragment;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Path;
import android.graphics.PixelFormat;
import android.graphics.Rect;
import android.hardware.display.DisplayManager;
import android.provider.Settings.Secure;
import android.support.annotation.VisibleForTesting;
import android.util.DisplayMetrics;
import android.view.DisplayCutout;
import android.view.DisplayInfo;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.View;
@@ -33,21 +44,24 @@ import android.view.View.OnLayoutChangeListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.ImageView;

import com.android.systemui.R.id;
import com.android.systemui.fragments.FragmentHostManager;
import com.android.systemui.fragments.FragmentHostManager.FragmentListener;
import com.android.systemui.plugins.qs.QS;
import com.android.systemui.qs.SecureSetting;
import com.android.systemui.statusbar.phone.CollapsedStatusBarFragment;
import com.android.systemui.statusbar.phone.NavigationBarFragment;
import com.android.systemui.statusbar.phone.StatusBar;
import com.android.systemui.tuner.TunablePadding;
import com.android.systemui.tuner.TunerService;
import com.android.systemui.tuner.TunerService.Tunable;

public class RoundedCorners extends SystemUI implements Tunable {
/**
 * An overlay that draws screen decorations in software (e.g for rounded corners or display cutout)
 * for antialiasing and emulation purposes.
 */
public class ScreenDecorations extends SystemUI implements Tunable {
    public static final String SIZE = "sysui_rounded_size";
    public static final String PADDING = "sysui_rounded_content_padding";

@@ -55,16 +69,16 @@ public class RoundedCorners extends SystemUI implements Tunable {
    private View mOverlay;
    private View mBottomOverlay;
    private float mDensity;
    private TunablePadding mQsPadding;
    private TunablePadding mStatusBarPadding;
    private TunablePadding mNavBarPadding;
    private WindowManager mWindowManager;
    private boolean mLandscape;

    @Override
    public void start() {
        mWindowManager = mContext.getSystemService(WindowManager.class);
        mRoundedDefault = mContext.getResources().getDimensionPixelSize(
                R.dimen.rounded_corner_radius);
        if (mRoundedDefault != 0) {
            setupRounding();
        if (mRoundedDefault != 0 || shouldDrawCutout()) {
            setupDecorations();
        }
        int padding = mContext.getResources().getDimensionPixelSize(
                R.dimen.rounded_corner_content_padding);
@@ -73,28 +87,29 @@ public class RoundedCorners extends SystemUI implements Tunable {
        }
    }

    private void setupRounding() {
    private void setupDecorations() {
        mOverlay = LayoutInflater.from(mContext)
                .inflate(R.layout.rounded_corners, null);
        ((ViewGroup)mOverlay).addView(new DisplayCutoutView(mContext, true,
                this::updateWindowVisibilities));
        mBottomOverlay = LayoutInflater.from(mContext)
                .inflate(R.layout.rounded_corners, null);
        ((ViewGroup)mBottomOverlay).addView(new DisplayCutoutView(mContext, false,
                this::updateWindowVisibilities));

        mOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        mOverlay.setAlpha(0);
        mOverlay.findViewById(R.id.right).setRotation(90);

        mContext.getSystemService(WindowManager.class)
                .addView(mOverlay, getWindowLayoutParams());
        mBottomOverlay = LayoutInflater.from(mContext)
                .inflate(R.layout.rounded_corners, null);
        mBottomOverlay.setSystemUiVisibility(View.SYSTEM_UI_FLAG_LAYOUT_STABLE);
        mBottomOverlay.setAlpha(0);
        mBottomOverlay.findViewById(R.id.right).setRotation(180);
        mBottomOverlay.findViewById(R.id.left).setRotation(270);
        WindowManager.LayoutParams layoutParams = getWindowLayoutParams();
        layoutParams.gravity = Gravity.BOTTOM;
        mContext.getSystemService(WindowManager.class)
                .addView(mBottomOverlay, layoutParams);

        updateViews();

        mWindowManager.addView(mOverlay, getWindowLayoutParams());
        mWindowManager.addView(mBottomOverlay, getBottomLayoutParams());

        DisplayMetrics metrics = new DisplayMetrics();
        mContext.getSystemService(WindowManager.class)
                .getDefaultDisplay().getMetrics(metrics);
        mWindowManager.getDefaultDisplay().getMetrics(metrics);
        mDensity = metrics.density;

        Dependency.get(TunerService.class).addTunable(this, SIZE);
@@ -106,10 +121,10 @@ public class RoundedCorners extends SystemUI implements Tunable {
            protected void handleValueChanged(int value, boolean observedChange) {
                int tint = value != 0 ? Color.WHITE : Color.BLACK;
                ColorStateList tintList = ColorStateList.valueOf(tint);
                ((ImageView) mOverlay.findViewById(id.left)).setImageTintList(tintList);
                ((ImageView) mOverlay.findViewById(id.right)).setImageTintList(tintList);
                ((ImageView) mBottomOverlay.findViewById(id.left)).setImageTintList(tintList);
                ((ImageView) mBottomOverlay.findViewById(id.right)).setImageTintList(tintList);
                ((ImageView) mOverlay.findViewById(R.id.left)).setImageTintList(tintList);
                ((ImageView) mOverlay.findViewById(R.id.right)).setImageTintList(tintList);
                ((ImageView) mBottomOverlay.findViewById(R.id.left)).setImageTintList(tintList);
                ((ImageView) mBottomOverlay.findViewById(R.id.right)).setImageTintList(tintList);
            }
        };
        setting.setListening(true);
@@ -133,6 +148,64 @@ public class RoundedCorners extends SystemUI implements Tunable {
        });
    }

    @Override
    protected void onConfigurationChanged(Configuration newConfig) {
        boolean newLanscape = newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE;
        if (newLanscape != mLandscape) {
            mLandscape = newLanscape;

            if (mOverlay != null) {
                updateLayoutParams();
                updateViews();
            }
        }
        if (shouldDrawCutout() && mOverlay == null) {
            setupDecorations();
        }
    }

    private void updateViews() {
        View topLeft = mOverlay.findViewById(R.id.left);
        View topRight = mOverlay.findViewById(R.id.right);
        View bottomLeft = mBottomOverlay.findViewById(R.id.left);
        View bottomRight = mBottomOverlay.findViewById(R.id.right);
        if (mLandscape) {
            // Flip corners
            View tmp = topRight;
            topRight = bottomLeft;
            bottomLeft = tmp;
        }
        updateView(topLeft, Gravity.TOP | Gravity.LEFT, 0);
        updateView(topRight, Gravity.TOP | Gravity.RIGHT, 90);
        updateView(bottomLeft, Gravity.BOTTOM | Gravity.LEFT, 270);
        updateView(bottomRight, Gravity.BOTTOM | Gravity.RIGHT, 180);

        updateWindowVisibilities();
    }

    private void updateView(View v, int gravity, int rotation) {
        ((FrameLayout.LayoutParams)v.getLayoutParams()).gravity = gravity;
        v.setRotation(rotation);
    }

    private void updateWindowVisibilities() {
        updateWindowVisibility(mOverlay);
        updateWindowVisibility(mBottomOverlay);
    }

    private void updateWindowVisibility(View overlay) {
        boolean visibleForCutout = shouldDrawCutout()
                && overlay.findViewById(R.id.display_cutout).getVisibility() == View.VISIBLE;
        boolean visibleForRoundedCorners = mRoundedDefault > 0;
        overlay.setVisibility(visibleForCutout || visibleForRoundedCorners
                ? View.VISIBLE : View.GONE);
    }

    private boolean shouldDrawCutout() {
        return mContext.getResources().getBoolean(
                com.android.internal.R.bool.config_fillMainBuiltInDisplayCutout);
    }

    private void setupPadding(int padding) {
        // Add some padding to all the content near the edge of the screen.
        StatusBar sb = getComponent(StatusBar.class);
@@ -149,7 +222,8 @@ public class RoundedCorners extends SystemUI implements Tunable {
        }
    }

    private WindowManager.LayoutParams getWindowLayoutParams() {
    @VisibleForTesting
    WindowManager.LayoutParams getWindowLayoutParams() {
        final WindowManager.LayoutParams lp = new WindowManager.LayoutParams(
                ViewGroup.LayoutParams.MATCH_PARENT,
                LayoutParams.WRAP_CONTENT,
@@ -163,12 +237,27 @@ public class RoundedCorners extends SystemUI implements Tunable {
                PixelFormat.TRANSLUCENT);
        lp.privateFlags |= WindowManager.LayoutParams.PRIVATE_FLAG_SHOW_FOR_ALL_USERS
                | WindowManager.LayoutParams.PRIVATE_FLAG_IS_ROUNDED_CORNERS_OVERLAY;
        lp.setTitle("RoundedOverlay");
        lp.gravity = Gravity.TOP;
        lp.setTitle("ScreenDecorOverlay");
        lp.gravity = Gravity.TOP | Gravity.LEFT;
        lp.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
        if (mLandscape) {
            lp.width = WRAP_CONTENT;
            lp.height = MATCH_PARENT;
        }
        return lp;
    }

    private WindowManager.LayoutParams getBottomLayoutParams() {
        WindowManager.LayoutParams lp = getWindowLayoutParams();
        lp.setTitle("ScreenDecorOverlayBottom");
        lp.gravity = Gravity.BOTTOM | Gravity.RIGHT;
        return lp;
    }

    private void updateLayoutParams() {
        mWindowManager.updateViewLayout(mOverlay, getWindowLayoutParams());
        mWindowManager.updateViewLayout(mBottomOverlay, getBottomLayoutParams());
    }

    @Override
    public void onTuningChanged(String key, String newValue) {
@@ -218,4 +307,109 @@ public class RoundedCorners extends SystemUI implements Tunable {
                    FLAG_START | FLAG_END);
        }
    }

    public static class DisplayCutoutView extends View implements DisplayManager.DisplayListener {

        private final DisplayInfo mInfo = new DisplayInfo();
        private final Paint mPaint = new Paint();
        private final Rect mBoundingRect = new Rect();
        private final Path mBoundingPath = new Path();
        private final int[] mLocation = new int[2];
        private final boolean mStart;
        private final Runnable mVisibilityChangedListener;

        public DisplayCutoutView(Context context, boolean start,
                Runnable visibilityChangedListener) {
            super(context);
            mStart = start;
            mVisibilityChangedListener = visibilityChangedListener;
            setId(R.id.display_cutout);
        }

        @Override
        protected void onAttachedToWindow() {
            super.onAttachedToWindow();
            mContext.getSystemService(DisplayManager.class).registerDisplayListener(this,
                    getHandler());
            update();
        }

        @Override
        protected void onDetachedFromWindow() {
            super.onDetachedFromWindow();
            mContext.getSystemService(DisplayManager.class).unregisterDisplayListener(this);
        }

        @Override
        protected void onDraw(Canvas canvas) {
            super.onDraw(canvas);
            getLocationOnScreen(mLocation);
            canvas.translate(-mLocation[0], -mLocation[1]);
            if (!mBoundingPath.isEmpty()) {
                mPaint.setColor(Color.BLACK);
                mPaint.setStyle(Paint.Style.FILL);
                canvas.drawPath(mBoundingPath, mPaint);
            }
        }

        @Override
        public void onDisplayAdded(int displayId) {
        }

        @Override
        public void onDisplayRemoved(int displayId) {
        }

        @Override
        public void onDisplayChanged(int displayId) {
            if (displayId == getDisplay().getDisplayId()) {
                update();
            }
        }

        private void update() {
            requestLayout();
            getDisplay().getDisplayInfo(mInfo);
            mBoundingRect.setEmpty();
            mBoundingPath.reset();
            int newVisible;
            if (hasCutout()) {
                mBoundingRect.set(mInfo.displayCutout.getBoundingRect());
                mInfo.displayCutout.getBounds().getBoundaryPath(mBoundingPath);
                newVisible = VISIBLE;
            } else {
                newVisible = GONE;
            }
            if (newVisible != getVisibility()) {
                setVisibility(newVisible);
                mVisibilityChangedListener.run();
            }
        }

        private boolean hasCutout() {
            if (mInfo.displayCutout == null) {
                return false;
            }
            DisplayCutout displayCutout = mInfo.displayCutout.calculateRelativeTo(
                    new Rect(0, 0, mInfo.logicalWidth, mInfo.logicalHeight));
            if (mStart) {
                return displayCutout.getSafeInsetLeft() > 0
                        || displayCutout.getSafeInsetTop() > 0;
            } else {
                return displayCutout.getSafeInsetRight() > 0
                        || displayCutout.getSafeInsetBottom() > 0;
            }
        }

        @Override
        protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
            if (mBoundingRect.isEmpty()) {
                super.onMeasure(widthMeasureSpec, heightMeasureSpec);
                return;
            }
            setMeasuredDimension(
                    resolveSizeAndState(mBoundingRect.width(), widthMeasureSpec, 0),
                    resolveSizeAndState(mBoundingRect.height(), heightMeasureSpec, 0));
        }
    }
}
Loading