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