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
This commit is contained in:
Agi Sferro 2021-05-20 15:58:02 +00:00
Родитель a195ad2fcf
Коммит 3e525ec94e
1 изменённых файлов: 64 добавлений и 27 удалений

Просмотреть файл

@ -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;
}
}