From 3e525ec94ebf56350c277febe4a367f261c08ef8 Mon Sep 17 00:00:00 2001 From: Agi Sferro Date: Thu, 20 May 2021 15:58:02 +0000 Subject: [PATCH] Bug 1707959 - Set TYPE_GLOW overscroll type on Android 12. r=droeh Android 12 introduces a new type of overscroll effect named TYPE_STRETCH which stretches the whole surface when overscrolling. Unforunately, given how it's implemented, SurfaceView does not support this type of effect, similarly to many other View effects. The OverscrollEffect is currently crashing on Android 12 builds so we restore the type to the previous (and only) default to fix the crash. Note that Overscroll is still not displayed, I'll open a new bug to fix that part. This patch also avoids using reflection for setBlendMode on API 29 and later where an API is actually available. Using reflection to access private APIs is not available since Android 9 so this code was a no-op before this patch. Differential Revision: https://phabricator.services.mozilla.com/D115031 --- .../geckoview/OverscrollEdgeEffect.java | 91 +++++++++++++------ 1 file changed, 64 insertions(+), 27 deletions(-) diff --git a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java index 6c5a020a4054..fc9e1ab833b2 100644 --- a/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java +++ b/mobile/android/geckoview/src/main/java/org/mozilla/geckoview/OverscrollEdgeEffect.java @@ -8,6 +8,7 @@ package org.mozilla.geckoview; import org.mozilla.gecko.util.ThreadUtils; import android.content.Context; +import android.graphics.BlendMode; import android.graphics.Canvas; import android.graphics.Paint; import android.graphics.PorterDuff; @@ -17,9 +18,13 @@ import android.os.Build; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.UiThread; +import androidx.core.os.BuildCompat; + import android.widget.EdgeEffect; import java.lang.reflect.Field; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; @UiThread public final class OverscrollEdgeEffect { @@ -44,6 +49,61 @@ public final class OverscrollEdgeEffect { mSession = session; } + private static Field sPaintField; + private static Method sSetType; + + // By default on SDK_INT 31 and above the edge effect default changed to "TYPE_STRETCH" + // which is an effect that we can't support due to using SurfaceTexture. + // This restores the edge effect type to TYPE_GLOW which is the default (and only option) on + // lower versions. + private void setType(final EdgeEffect edgeEffect) { + if (Build.VERSION.SDK_INT < 31 && !Build.VERSION.CODENAME.equals("S")) { + // setType is only available on 31 (early builds advertise themselves as 30, + // with codename S) + return; + } + + // TODO: remove reflection once 31 is stable + if (sSetType == null) { + try { + sSetType = EdgeEffect.class.getDeclaredMethod("setType", int.class); + } catch (NoSuchMethodException e) { + // Nothing we can do here + return; + } + } + + try { + sSetType.invoke(edgeEffect, /* TYPE_GLOW */ 0); + } catch (Exception ex) { + } + } + + private void setBlendMode(EdgeEffect edgeEffect) { + if (Build.VERSION.SDK_INT >= 29) { + edgeEffect.setBlendMode(BlendMode.SRC); + return; + } + + if (sPaintField == null) { + try { + sPaintField = EdgeEffect.class.getDeclaredField("mPaint"); + sPaintField.setAccessible(true); + } catch (final NoSuchFieldException e) { + // Cannot get the field, nothing we can do here + return; + } + } + + try { + final Paint paint = (Paint) sPaintField.get(edgeEffect); + final PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC); + paint.setXfermode(mode); + } catch (final IllegalAccessException ex) { + // Nothing we can do + } + } + /** * Set the theme to use for overscroll from a given Context. * @@ -52,34 +112,11 @@ public final class OverscrollEdgeEffect { public void setTheme(final @NonNull Context context) { ThreadUtils.assertOnUiThread(); - final PorterDuffXfermode mode = new PorterDuffXfermode(PorterDuff.Mode.SRC); - Field paintField = null; - - if (Build.VERSION.SDK_INT >= 21) { - try { - paintField = EdgeEffect.class.getDeclaredField("mPaint"); - paintField.setAccessible(true); - } catch (final NoSuchFieldException e) { - } - } - for (int i = 0; i < mEdges.length; i++) { - mEdges[i] = new EdgeEffect(context); - - if (paintField == null) { - continue; - } - - try { - final Paint p = (Paint) paintField.get(mEdges[i]); - - // The Android EdgeEffect class uses a mode of SRC_ATOP here, which means - // it will only draw the effect where there are non-transparent pixels in - // the destination. Since the LayerView itself is fully transparent, it - // doesn't display at all. We need to use SRC instead. - p.setXfermode(mode); - } catch (final IllegalAccessException e) { - } + final EdgeEffect edgeEffect = new EdgeEffect(context); + setBlendMode(edgeEffect); + setType(edgeEffect); + mEdges[i] = edgeEffect; } }