128 lines
4.0 KiB
Plaintext
128 lines
4.0 KiB
Plaintext
import { UTSAndroid } from "io.dcloud.uts";
|
||
import PixelCopy from "android.view.PixelCopy";
|
||
import OnPixelCopyFinishedListener from "android.view.PixelCopy.OnPixelCopyFinishedListener";
|
||
import Bitmap from "android.graphics.Bitmap";
|
||
import Canvas from "android.graphics.Canvas";
|
||
import Handler from "android.os.Handler";
|
||
import Looper from "android.os.Looper";
|
||
import Build from "android.os.Build";
|
||
import ByteArrayOutputStream from "java.io.ByteArrayOutputStream";
|
||
import Base64 from "android.util.Base64";
|
||
import {
|
||
CaptureScreen,
|
||
CaptureScreenOptions,
|
||
CaptureScreenSuccess,
|
||
CaptureScreenFail
|
||
} from "../interface.uts";
|
||
|
||
/**
|
||
* 将 Bitmap 压缩为 JPEG 并编码为 data URL 形式的 base64
|
||
*/
|
||
function encodeBitmapToBase64(bitmap : Bitmap) : string {
|
||
const baos = new ByteArrayOutputStream();
|
||
bitmap.compress(Bitmap.CompressFormat.JPEG, 80, baos);
|
||
const bytes = baos.toByteArray();
|
||
const base64 = Base64.encodeToString(bytes, Base64.NO_WRAP);
|
||
bitmap.recycle();
|
||
return "data:image/jpeg;base64," + base64;
|
||
}
|
||
|
||
/**
|
||
* PixelCopy 完成监听器
|
||
*/
|
||
class PixelCopyListener extends OnPixelCopyFinishedListener {
|
||
|
||
private bitmap : Bitmap;
|
||
private options : CaptureScreenOptions;
|
||
|
||
constructor(bitmap : Bitmap, options : CaptureScreenOptions) {
|
||
super();
|
||
this.bitmap = bitmap;
|
||
this.options = options;
|
||
}
|
||
|
||
override onPixelCopyFinished(copyResult : Int) : void {
|
||
if (copyResult == PixelCopy.SUCCESS) {
|
||
const base64 = encodeBitmapToBase64(this.bitmap);
|
||
const res : CaptureScreenSuccess = { base64: base64 };
|
||
this.options.success?.(res);
|
||
this.options.complete?.(res);
|
||
} else {
|
||
this.bitmap.recycle();
|
||
const res : CaptureScreenFail = {
|
||
errCode: 12010,
|
||
errMsg: "captureScreen:fail PixelCopy copyResult=" + copyResult
|
||
};
|
||
this.options.fail?.(res);
|
||
this.options.complete?.(res);
|
||
}
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 在 UI 线程执行实际截图
|
||
*/
|
||
class CaptureRunnable extends Runnable {
|
||
|
||
private options : CaptureScreenOptions;
|
||
|
||
constructor(options : CaptureScreenOptions) {
|
||
super();
|
||
this.options = options;
|
||
}
|
||
|
||
override run() : void {
|
||
try {
|
||
const activity = UTSAndroid.getUniActivity();
|
||
if (activity == null) {
|
||
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail activity is null" };
|
||
this.options.fail?.(res);
|
||
this.options.complete?.(res);
|
||
return;
|
||
}
|
||
// 注意:UTS 中 getWindow() 返回可空类型,必须用 ! 断言,否则编译失败
|
||
const window = activity!.getWindow()!;
|
||
const decorView = window.getDecorView()!;
|
||
const width = decorView.getWidth();
|
||
const height = decorView.getHeight();
|
||
if (width <= 0 || height <= 0) {
|
||
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail invalid window size" };
|
||
this.options.fail?.(res);
|
||
this.options.complete?.(res);
|
||
return;
|
||
}
|
||
|
||
const bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888);
|
||
|
||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||
// Android 8.0+:使用 PixelCopy 可以正确截取 SurfaceView/视频等硬件加速画面
|
||
const handler = new Handler(Looper.getMainLooper());
|
||
PixelCopy.request(window, bitmap, new PixelCopyListener(bitmap, this.options), handler);
|
||
} else {
|
||
// 低版本降级:仅能截取 View 层级内容,视频区域可能为黑屏
|
||
const canvas = new Canvas(bitmap);
|
||
decorView.draw(canvas);
|
||
const base64 = encodeBitmapToBase64(bitmap);
|
||
const res : CaptureScreenSuccess = { base64: base64 };
|
||
this.options.success?.(res);
|
||
this.options.complete?.(res);
|
||
}
|
||
} catch (e) {
|
||
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail " + e.toString() };
|
||
this.options.fail?.(res);
|
||
this.options.complete?.(res);
|
||
}
|
||
}
|
||
}
|
||
|
||
export const captureScreen : CaptureScreen = function (options : CaptureScreenOptions) {
|
||
const activity = UTSAndroid.getUniActivity();
|
||
if (activity == null) {
|
||
const res : CaptureScreenFail = { errCode: 12010, errMsg: "captureScreen:fail activity is null" };
|
||
options.fail?.(res);
|
||
options.complete?.(res);
|
||
return;
|
||
}
|
||
activity!.runOnUiThread(new CaptureRunnable(options));
|
||
}
|