Skip to content

Instantly share code, notes, and snippets.

@alexjlockwood
Created March 7, 2020 19:40
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save alexjlockwood/3857233659e9981632bab64f84ff282f to your computer and use it in GitHub Desktop.
Save alexjlockwood/3857233659e9981632bab64f84ff282f to your computer and use it in GitHub Desktop.
package com.instabug.library.instacapture.screenshot;
import android.annotation.TargetApi;
import android.app.Activity;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.Bitmap.Config;
import android.graphics.PorterDuff.Mode;
import android.opengl.GLSurfaceView;
import android.os.Build.VERSION;
import android.util.Pair;
import android.view.TextureView;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import androidx.annotation.IdRes;
import androidx.annotation.Nullable;
import com.instabug.library.Instabug;
import com.instabug.library.instacapture.c.a;
import com.instabug.library.settings.SettingsManager;
import com.instabug.library.util.DisplayUtils;
import com.instabug.library.util.InstabugSDKLogger;
import io.reactivex.Observable;
import io.reactivex.ObservableEmitter;
import io.reactivex.ObservableOnSubscribe;
import io.reactivex.ObservableSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import java.lang.ref.WeakReference;
import java.nio.IntBuffer;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map.Entry;
import java.util.concurrent.CountDownLatch;
import javax.microedition.khronos.egl.EGL10;
import javax.microedition.khronos.egl.EGLContext;
import javax.microedition.khronos.opengles.GL10;
public final class ScreenshotTaker {
private ScreenshotTaker() {
}
public static Observable<Bitmap> getScreenshotBitmap(Activity activity, @Nullable @IdRes int[] ignoredViewsIds) {
if (activity == null) {
throw new IllegalArgumentException("Parameter activity cannot be null.");
} else {
View var2 = activity.getWindow().getDecorView();
List var3 = FieldHelper.getRootViews(activity, ignoredViewsIds);
InstabugSDKLogger.d(ScreenshotTaker.class, "viewRoots count: " + var3.size());
Bitmap var4;
try {
if ((long)(var2.getWidth() * var2.getHeight() * 4) < a.a(activity)) {
var4 = Bitmap.createBitmap(var2.getWidth(), var2.getHeight(), Config.ARGB_8888);
} else {
var4 = Bitmap.createBitmap(var2.getWidth(), var2.getHeight(), Config.RGB_565);
}
} catch (OutOfMemoryError | IllegalArgumentException var6) {
return null;
}
return drawRootsToBitmap(var3, var4, ignoredViewsIds);
}
}
private static Observable<Bitmap> drawRootsToBitmap(List<RootViewInfo> viewRoots, final Bitmap bitmap, @Nullable @IdRes final int[] ignoredViewsIds) {
return Observable.fromIterable(viewRoots).flatMap(new Function<RootViewInfo, ObservableSource<Bitmap>>() {
public ObservableSource<Bitmap> a(RootViewInfo var1) throws Exception {
return ScreenshotTaker.drawRootToBitmap(var1, bitmap, ignoredViewsIds);
}
});
}
private static Observable<Bitmap> drawRootToBitmap(final RootViewInfo rootViewInfo, final Bitmap bitmap, @Nullable @IdRes final int[] ignoredViewsIds) {
return Observable.create(new ObservableOnSubscribe<Pair<Canvas, HashMap<View, Integer>>>() {
public void subscribe(ObservableEmitter<Pair<Canvas, HashMap<View, Integer>>> emitter) throws Exception {
Canvas var2;
if ((rootViewInfo.getLayoutParams().flags & 2) == 2) {
var2 = new Canvas(bitmap);
int var3 = (int)(255.0F * rootViewInfo.getLayoutParams().dimAmount);
var2.drawARGB(var3, 0, 0, 0);
}
var2 = new Canvas(bitmap);
var2.translate((float)rootViewInfo.getLeft(), (float)rootViewInfo.getTop());
HashMap var6 = new HashMap();
if (ignoredViewsIds != null) {
for(int var4 = 0; var4 < ignoredViewsIds.length; ++var4) {
View var5 = rootViewInfo.getView().findViewById(ignoredViewsIds[var4]);
if (var5 != null) {
var6.put(var5, var5.getVisibility());
}
}
}
emitter.onNext(new Pair(var2, var6));
}
}).subscribeOn(Schedulers.single()).observeOn(AndroidSchedulers.mainThread()).map(new Function<Pair<Canvas, HashMap<View, Integer>>, Pair<Canvas, HashMap<View, Integer>>>() {
public Pair<Canvas, HashMap<View, Integer>> a(Pair<Canvas, HashMap<View, Integer>> var1) throws Exception {
Iterator var2 = ((HashMap)var1.second).keySet().iterator();
while(var2.hasNext()) {
View var3 = (View)var2.next();
var3.setVisibility(4);
}
rootViewInfo.getView().draw((Canvas)var1.first);
return var1;
}
}).observeOn(Schedulers.single()).map(new Function<Pair<Canvas, HashMap<View, Integer>>, HashMap<View, Integer>>() {
public HashMap<View, Integer> a(Pair<Canvas, HashMap<View, Integer>> var1) throws Exception {
ScreenshotTaker.drawUnDrawableViews(rootViewInfo.getView(), (Canvas)var1.first);
Collection var2 = SettingsManager.getInstance().getPrivateViews();
HashSet var3 = new HashSet();
Iterator var4 = var2.iterator();
while(true) {
while(var4.hasNext()) {
WeakReference var5 = (WeakReference)var4.next();
if (var5 != null && var5.get() != null) {
View var6 = (View)var5.get();
if (var6 != null && ScreenshotTaker.isVisible(var6)) {
int[] var7 = new int[2];
var6.getLocationOnScreen(var7);
((Canvas)var1.first).drawRect(ScreenshotTaker.getVisibleRect(var6), new Paint());
}
} else {
var3.add(var5);
}
}
SettingsManager.getInstance().getPrivateViews().removeAll(var3);
return (HashMap)var1.second;
}
}
}).observeOn(AndroidSchedulers.mainThread()).map(new Function<HashMap<View, Integer>, Bitmap>() {
public Bitmap a(HashMap<View, Integer> var1) throws Exception {
Iterator var2 = var1.entrySet().iterator();
while(var2.hasNext()) {
Entry var3 = (Entry)var2.next();
((View)var3.getKey()).setVisibility((Integer)var3.getValue());
}
return bitmap;
}
});
}
public static boolean isVisible(View view) {
if (!view.isShown()) {
return false;
} else {
Rect var1 = new Rect();
view.getGlobalVisibleRect(var1);
Rect var2 = new Rect(0, 0, DisplayUtils.getDisplayWidthInPx(Instabug.getApplicationContext()), DisplayUtils.getDisplayHeightInPx(Instabug.getApplicationContext()));
return var1.intersect(var2);
}
}
private static Rect getVisibleRect(@Nullable View childView) {
Rect var1 = new Rect();
if (childView != null && childView.getVisibility() == 0 && childView.getRootView().getParent() != null) {
return !childView.getGlobalVisibleRect(var1) ? new Rect(0, 0, 0, 0) : var1;
} else {
return new Rect(0, 0, 0, 0);
}
}
private static ArrayList<View> drawUnDrawableViews(View v, Canvas canvas) {
ArrayList var2;
if (!(v instanceof ViewGroup)) {
var2 = new ArrayList();
var2.add(v);
return var2;
} else {
var2 = new ArrayList();
ViewGroup var3 = (ViewGroup)v;
for(int var4 = 0; var4 < var3.getChildCount(); ++var4) {
View var5 = var3.getChildAt(var4);
ArrayList var6 = new ArrayList();
var6.add(v);
var6.addAll(drawUnDrawableViews(var5, canvas));
int[] var7 = new int[2];
var5.getLocationOnScreen(var7);
if (VERSION.SDK_INT >= 14 && var5 instanceof TextureView) {
drawTextureView((TextureView)var5, var7, canvas);
}
if (var5 instanceof GLSurfaceView) {
drawGLSurfaceView((GLSurfaceView)var5, var7, canvas);
}
if (VERSION.SDK_INT >= 11 && var5 instanceof WebView) {
drawWebView((WebView)var5, canvas);
}
var2.addAll(var6);
}
return var2;
}
}
public static void drawGLSurfaceView(GLSurfaceView surfaceView, int[] locationOnScreen, Canvas canvas) {
InstabugSDKLogger.d(ScreenshotTaker.class, "Drawing GLSurfaceView");
if (surfaceView.getWindowToken() != null) {
final int var3 = surfaceView.getWidth();
final int var4 = surfaceView.getHeight();
boolean var5 = false;
boolean var6 = false;
int[] var7 = new int[var3 * (0 + var4)];
final IntBuffer var8 = IntBuffer.wrap(var7);
var8.position(0);
final CountDownLatch var9 = new CountDownLatch(1);
surfaceView.queueEvent(new Runnable() {
public void run() {
EGL10 var1 = (EGL10)EGLContext.getEGL();
var1.eglWaitGL();
GL10 var2 = (GL10)var1.eglGetCurrentContext().getGL();
var2.glFinish();
try {
Thread.sleep(200L);
} catch (InterruptedException var4x) {
var4x.printStackTrace();
}
var2.glReadPixels(0, 0, var3, 0 + var4, 6408, 5121, var8);
var9.countDown();
}
});
try {
var9.await();
} catch (InterruptedException var18) {
var18.printStackTrace();
}
int[] var10 = new int[var3 * var4];
int var11 = 0;
for(int var12 = 0; var11 < var4; ++var12) {
for(int var13 = 0; var13 < var3; ++var13) {
int var14 = var7[var11 * var3 + var13];
int var15 = var14 >> 16 & 255;
int var16 = var14 << 16 & 16711680;
int var17 = var14 & -16711936 | var16 | var15;
var10[(var4 - var12 - 1) * var3 + var13] = var17;
}
++var11;
}
Bitmap var19 = Bitmap.createBitmap(var10, var3, var4, Config.ARGB_8888);
Paint var20 = new Paint();
var20.setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
canvas.drawBitmap(var19, (float)locationOnScreen[0], (float)locationOnScreen[1], var20);
var19.recycle();
}
}
@TargetApi(14)
public static void drawTextureView(TextureView textureView, int[] locationOnScreen, Canvas canvas) {
InstabugSDKLogger.d(ScreenshotTaker.class, "Drawing TextureView");
try {
Bitmap var3 = textureView.getBitmap();
if (var3 != null) {
Paint var4 = new Paint();
var4.setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
canvas.drawBitmap(var3, (float)locationOnScreen[0], (float)locationOnScreen[1], var4);
var3.recycle();
}
} catch (OutOfMemoryError var5) {
InstabugSDKLogger.e(ScreenshotTaker.class, ScreenshotTaker.class.getSimpleName(), var5);
}
}
@TargetApi(11)
public static void drawWebView(WebView webView, Canvas canvas) {
int var2 = webView.getLayerType();
if (var2 == 2) {
webView.setLayerType(0, (Paint)null);
webView.setDrawingCacheEnabled(true);
webView.buildDrawingCache(true);
Bitmap var3 = webView.getDrawingCache();
if (var3 != null) {
Paint var4 = new Paint();
var4.setXfermode(new PorterDuffXfermode(Mode.DST_ATOP));
int[] var5 = new int[2];
webView.getLocationOnScreen(var5);
canvas.drawBitmap(var3, (float)var5[0], (float)var5[1], var4);
var3.recycle();
}
webView.setDrawingCacheEnabled(false);
webView.setLayerType(var2, (Paint)null);
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment