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

Commit 927fe872 authored by Issei Suzuki's avatar Issei Suzuki
Browse files

DO NOT MERGE: Support alpha value in SurfaceView.

For backward compatibility, SurfaceView ignores alpha value by
default. In order to reflect alpha value set on the SurfaceView
to its underlying surface, setUseAlpha() needs to be called.

Translucent alpha only works when the surface is placed z-above.
Otherwise only fully opaque and transparent status are supported.

Bug: 130442248
Test: Manual, use BubblesTest app and checks if alpha is set.
Change-Id: I86847de59109b2adf12a2c7c50c988c2cbcf0450
parent 03c2c102
Loading
Loading
Loading
Loading
+1 −0
Original line number Original line Diff line number Diff line
@@ -120,6 +120,7 @@ public class ActivityView extends ViewGroup {


        mActivityTaskManager = ActivityTaskManager.getService();
        mActivityTaskManager = ActivityTaskManager.getService();
        mSurfaceView = new SurfaceView(context);
        mSurfaceView = new SurfaceView(context);
        mSurfaceView.setUseAlpha();
        mSurfaceView.setAlpha(0f);
        mSurfaceView.setAlpha(0f);
        mSurfaceCallback = new SurfaceCallback();
        mSurfaceCallback = new SurfaceCallback();
        mSurfaceView.getHolder().addCallback(mSurfaceCallback);
        mSurfaceView.getHolder().addCallback(mSurfaceCallback);
+189 −31
Original line number Original line Diff line number Diff line
@@ -99,6 +99,7 @@ import java.util.concurrent.locks.ReentrantLock;
public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallback {
    private static final String TAG = "SurfaceView";
    private static final String TAG = "SurfaceView";
    private static final boolean DEBUG = false;
    private static final boolean DEBUG = false;
    private static final boolean DEBUG_POSITION = false;


    @UnsupportedAppUsage
    @UnsupportedAppUsage
    final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>();
    final ArrayList<SurfaceHolder.Callback> mCallbacks = new ArrayList<>();
@@ -124,6 +125,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
    // we need to preserve the old one until the new one has drawn.
    // we need to preserve the old one until the new one has drawn.
    SurfaceControl mDeferredDestroySurfaceControl;
    SurfaceControl mDeferredDestroySurfaceControl;
    SurfaceControl mBackgroundControl;
    SurfaceControl mBackgroundControl;
    final Object mSurfaceControlLock = new Object();
    final Rect mTmpRect = new Rect();
    final Rect mTmpRect = new Rect();


    Paint mRoundedViewportPaint;
    Paint mRoundedViewportPaint;
@@ -161,6 +163,9 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
    @UnsupportedAppUsage
    @UnsupportedAppUsage
    int mRequestedFormat = PixelFormat.RGB_565;
    int mRequestedFormat = PixelFormat.RGB_565;


    boolean mUseAlpha = false;
    float mSurfaceAlpha = 1f;

    @UnsupportedAppUsage
    @UnsupportedAppUsage
    boolean mHaveFrame = false;
    boolean mHaveFrame = false;
    boolean mSurfaceCreated = false;
    boolean mSurfaceCreated = false;
@@ -276,6 +281,152 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        updateSurface();
        updateSurface();
    }
    }


    /**
     * Make alpha value of this view reflect onto the surface. This can only be called from at most
     * one SurfaceView within a view tree.
     *
     * <p class="note"><strong>Note:</strong> Alpha value of the view is ignored and the underlying
     * surface is rendered opaque by default.</p>
     *
     * @hide
     */
    public void setUseAlpha() {
        if (!mUseAlpha) {
            mUseAlpha = true;
            updateSurfaceAlpha();
        }
    }

    @Override
    public void setAlpha(float alpha) {
        // Sets the opacity of the view to a value, where 0 means the view is completely transparent
        // and 1 means the view is completely opaque.
        //
        // Note: Alpha value of this view is ignored by default. To enable alpha blending, you need
        // to call setUseAlpha() as well.
        // This view doesn't support translucent opacity if the view is located z-below, since the
        // logic to punch a hole in the view hierarchy cannot handle such case. See also
        // #clearSurfaceViewPort(Canvas)
        if (DEBUG) {
            Log.d(TAG, System.identityHashCode(this)
                    + " setAlpha: mUseAlpha = " + mUseAlpha + " alpha=" + alpha);
        }
        super.setAlpha(alpha);
        updateSurfaceAlpha();
    }

    private float getFixedAlpha() {
        // Compute alpha value to be set on the underlying surface.
        final float alpha = getAlpha();
        return mUseAlpha && (mSubLayer > 0 || alpha == 0f) ? alpha : 1f;
    }

    private void updateSurfaceAlpha() {
        if (!mUseAlpha) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + " updateSurfaceAlpha: setUseAlpha() is not called, ignored.");
            }
            return;
        }
        final float viewAlpha = getAlpha();
        if (mSubLayer < 0 && 0f < viewAlpha && viewAlpha < 1f) {
            Log.w(TAG, System.identityHashCode(this)
                    + " updateSurfaceAlpha:"
                    + " translucent color is not supported for a surface placed z-below.");
        }
        if (!mHaveFrame) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + " updateSurfaceAlpha: has no surface.");
            }
            return;
        }
        final ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot == null) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + " updateSurfaceAlpha: ViewRootImpl not available.");
            }
            return;
        }
        if (mSurfaceControl == null) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + "updateSurfaceAlpha:"
                        + " surface is not yet created, or already released.");
            }
            return;
        }
        final Surface parent = viewRoot.mSurface;
        if (parent == null || !parent.isValid()) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + " updateSurfaceAlpha: ViewRootImpl has no valid surface");
            }
            return;
        }
        final float alpha = getFixedAlpha();
        if (alpha != mSurfaceAlpha) {
            if (isHardwareAccelerated()) {
                /*
                 * Schedule a callback that reflects an alpha value onto the underlying surfaces.
                 * This gets called on a RenderThread worker thread, so members accessed here must
                 * be protected by a lock.
                 */
                viewRoot.registerRtFrameCallback(frame -> {
                    try {
                        final SurfaceControl.Transaction t = new SurfaceControl.Transaction();
                        synchronized (mSurfaceControlLock) {
                            if (!parent.isValid()) {
                                if (DEBUG) {
                                    Log.d(TAG, System.identityHashCode(this)
                                            + " updateSurfaceAlpha RT:"
                                            + " ViewRootImpl has no valid surface");
                                }
                                return;
                            }
                            if (mSurfaceControl == null) {
                                if (DEBUG) {
                                    Log.d(TAG, System.identityHashCode(this)
                                            + "updateSurfaceAlpha RT:"
                                            + " mSurfaceControl has already released");
                                }
                                return;
                            }
                            if (DEBUG) {
                                Log.d(TAG, System.identityHashCode(this)
                                        + " updateSurfaceAlpha RT: set alpha=" + alpha);
                            }
                            t.setAlpha(mSurfaceControl, alpha);
                            t.deferTransactionUntilSurface(mSurfaceControl, parent, frame);
                        }
                        // It's possible that mSurfaceControl is released in the UI thread before
                        // the transaction completes. If that happens, an exception is thrown, which
                        // must be caught immediately.
                        t.apply();
                    } catch (Exception e) {
                        Log.e(TAG, System.identityHashCode(this)
                                + "updateSurfaceAlpha RT: Exception during surface transaction", e);
                    }
                });
                damageInParent();
            } else {
                if (DEBUG) {
                    Log.d(TAG, System.identityHashCode(this)
                            + " updateSurfaceAlpha: set alpha=" + alpha);
                }
                SurfaceControl.openTransaction();
                try {
                    mSurfaceControl.setAlpha(alpha);
                } finally {
                    SurfaceControl.closeTransaction();
                }
            }
            mSurfaceAlpha = alpha;
        }
    }

    private void performDrawFinished() {
    private void performDrawFinished() {
        if (mPendingReportDraws > 0) {
        if (mPendingReportDraws > 0) {
            mDrawFinished = true;
            mDrawFinished = true;
@@ -325,11 +476,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        mRequestedVisible = false;
        mRequestedVisible = false;


        updateSurface();
        updateSurface();
        if (mSurfaceControl != null) {
        releaseSurfaces();
            mTmpTransaction.remove(mSurfaceControl).apply();
        }
        mSurfaceControl = null;

        mHaveFrame = false;
        mHaveFrame = false;


        super.onDetachedFromWindow();
        super.onDetachedFromWindow();
@@ -498,15 +645,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        }
        }
    }
    }


    private Rect getParentSurfaceInsets() {
        final ViewRootImpl root = getViewRootImpl();
        if (root == null) {
            return null;
        } else {
            return root.mWindowAttributes.surfaceInsets;
        }
    }

    private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) {
    private void updateBackgroundVisibilityInTransaction(SurfaceControl viewRoot) {
        if (mBackgroundControl == null) {
        if (mBackgroundControl == null) {
            return;
            return;
@@ -520,6 +658,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
    }
    }


    private void releaseSurfaces() {
    private void releaseSurfaces() {
        synchronized (mSurfaceControlLock) {
            if (mSurfaceControl != null) {
            if (mSurfaceControl != null) {
                mTmpTransaction.remove(mSurfaceControl);
                mTmpTransaction.remove(mSurfaceControl);
                mSurfaceControl = null;
                mSurfaceControl = null;
@@ -530,14 +669,23 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
            }
            }
            mTmpTransaction.apply();
            mTmpTransaction.apply();
        }
        }
        mSurfaceAlpha = 1f;
    }


    /** @hide */
    /** @hide */
    protected void updateSurface() {
    protected void updateSurface() {
        if (!mHaveFrame) {
        if (!mHaveFrame) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this) + " updateSurface: has no frame");
            }
            return;
            return;
        }
        }
        ViewRootImpl viewRoot = getViewRootImpl();
        ViewRootImpl viewRoot = getViewRootImpl();
        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
        if (viewRoot == null || viewRoot.mSurface == null || !viewRoot.mSurface.isValid()) {
            if (DEBUG) {
                Log.d(TAG, System.identityHashCode(this)
                        + " updateSurface: no valid surface");
            }
            return;
            return;
        }
        }


@@ -551,20 +699,25 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        int myHeight = mRequestedHeight;
        int myHeight = mRequestedHeight;
        if (myHeight <= 0) myHeight = getHeight();
        if (myHeight <= 0) myHeight = getHeight();


        final float alpha = getFixedAlpha();
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean formatChanged = mFormat != mRequestedFormat;
        final boolean visibleChanged = mVisible != mRequestedVisible;
        final boolean visibleChanged = mVisible != mRequestedVisible;
        final boolean alphaChanged = mSurfaceAlpha != alpha;
        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
        final boolean creating = (mSurfaceControl == null || formatChanged || visibleChanged)
                && mRequestedVisible;
                && mRequestedVisible;
        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
        final boolean sizeChanged = mSurfaceWidth != myWidth || mSurfaceHeight != myHeight;
        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
        final boolean windowVisibleChanged = mWindowVisibility != mLastWindowVisibility;
        boolean redrawNeeded = false;
        boolean redrawNeeded = false;


        if (creating || formatChanged || sizeChanged || visibleChanged || windowVisibleChanged) {
        if (creating || formatChanged || sizeChanged || visibleChanged || (mUseAlpha
                && alphaChanged) || windowVisibleChanged) {
            getLocationInWindow(mLocation);
            getLocationInWindow(mLocation);


            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
            if (DEBUG) Log.i(TAG, System.identityHashCode(this) + " "
                    + "Changes: creating=" + creating
                    + "Changes: creating=" + creating
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " format=" + formatChanged + " size=" + sizeChanged
                    + " visible=" + visibleChanged + " alpha=" + alphaChanged
                    + " mUseAlpha=" + mUseAlpha
                    + " visible=" + visibleChanged
                    + " visible=" + visibleChanged
                    + " left=" + (mWindowSpaceLeft != mLocation[0])
                    + " left=" + (mWindowSpaceLeft != mLocation[0])
                    + " top=" + (mWindowSpaceTop != mLocation[1]));
                    + " top=" + (mWindowSpaceTop != mLocation[1]));
@@ -586,7 +739,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
                    translator.translateRectInAppWindowToScreen(mScreenRect);
                    translator.translateRectInAppWindowToScreen(mScreenRect);
                }
                }


                final Rect surfaceInsets = getParentSurfaceInsets();
                final Rect surfaceInsets = viewRoot.mWindowAttributes.surfaceInsets;
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);
                mScreenRect.offset(surfaceInsets.left, surfaceInsets.top);


                if (creating) {
                if (creating) {
@@ -635,6 +788,10 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
                            mSurfaceControl.hide();
                            mSurfaceControl.hide();
                        }
                        }
                        updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
                        updateBackgroundVisibilityInTransaction(viewRoot.getSurfaceControl());
                        if (mUseAlpha) {
                            mSurfaceControl.setAlpha(alpha);
                            mSurfaceAlpha = alpha;
                        }


                        // While creating the surface, we will set it's initial
                        // While creating the surface, we will set it's initial
                        // geometry. Outside of that though, we should generally
                        // geometry. Outside of that though, we should generally
@@ -777,7 +934,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
                    mIsCreating = false;
                    mIsCreating = false;
                    if (mSurfaceControl != null && !mSurfaceCreated) {
                    if (mSurfaceControl != null && !mSurfaceCreated) {
                        mSurface.release();
                        mSurface.release();

                        releaseSurfaces();
                        releaseSurfaces();
                    }
                    }
                }
                }
@@ -817,10 +973,13 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb


                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
                if (!isHardwareAccelerated() || !mRtHandlingPositionUpdates) {
                    try {
                    try {
                        if (DEBUG) Log.d(TAG, String.format("%d updateSurfacePosition UI, " +
                        if (DEBUG_POSITION) {
                                "position = [%d, %d, %d, %d]", System.identityHashCode(this),
                            Log.d(TAG, String.format("%d updateSurfacePosition UI, "
                                            + "position = [%d, %d, %d, %d]",
                                    System.identityHashCode(this),
                                    mScreenRect.left, mScreenRect.top,
                                    mScreenRect.left, mScreenRect.top,
                                    mScreenRect.right, mScreenRect.bottom));
                                    mScreenRect.right, mScreenRect.bottom));
                        }
                        setParentSpaceRectangle(mScreenRect, -1);
                        setParentSpaceRectangle(mScreenRect, -1);
                    } catch (Exception ex) {
                    } catch (Exception ex) {
                        Log.e(TAG, "Exception configuring surface", ex);
                        Log.e(TAG, "Exception configuring surface", ex);
@@ -871,7 +1030,6 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
        if (mViewVisibility) {
        if (mViewVisibility) {
            mRtTransaction.show(surface);
            mRtTransaction.show(surface);
        }
        }

    }
    }


    private void setParentSpaceRectangle(Rect position, long frameNumber) {
    private void setParentSpaceRectangle(Rect position, long frameNumber) {
@@ -912,7 +1070,7 @@ public class SurfaceView extends View implements ViewRootImpl.WindowStoppedCallb
                return;
                return;
            }
            }
            try {
            try {
                if (DEBUG) {
                if (DEBUG_POSITION) {
                    Log.d(TAG, String.format(
                    Log.d(TAG, String.format(
                            "%d updateSurfacePosition RenderWorker, frameNr = %d, "
                            "%d updateSurfacePosition RenderWorker, frameNr = %d, "
                                    + "position = [%d, %d, %d, %d]",
                                    + "position = [%d, %d, %d, %d]",