ZaiZai hace 2 años
padre
commit
bf7352de8a

+ 11 - 1
components/hc-sys/index.vue

@@ -11,7 +11,7 @@
 </template>
 
 <script setup>
-import {ref, watch} from "vue";
+import {onMounted, ref, watch} from "vue";
 
 const props = defineProps({
     ui: String,
@@ -31,6 +31,16 @@ watch(() => [
 ], ([val]) => {
     isNavBars.value = val
 })
+
+//渲染完成
+onMounted(() => {
+    //监听用户截屏事件
+    // #ifdef APP-PLUS
+    uni.onUserCaptureScreen((path) => {
+        console.log('用户截屏了', path)
+    });
+    // #endif
+})
 </script>
 
 <style scoped lang="scss">

+ 10 - 0
uni_modules/uni-usercapturescreen/changelog.md

@@ -0,0 +1,10 @@
+## 1.0.4(2023-03-24)
+新增开启/关闭防截屏功能
+## 1.0.3(2023-03-17)
+修复android平台 部分场景下js可能报错的问题
+## 1.0.2(2023-03-16)
+修复Android平台在小米设备无法监听的问题 修复Android平台调用uni.onUserCaptureScreen必然会触发回调的问题
+## 1.0.1(2022-10-27)
+修改插件描述
+## 1.0.0(2022-10-26)
+支持安卓、iOS、微信小程序平台

+ 18 - 0
uni_modules/uni-usercapturescreen/index.d.ts

@@ -0,0 +1,18 @@
+declare namespace UniNamespace {
+  type OnUserCaptureScreenCallback = (res?: { errMsg: string }) => void
+}
+
+declare interface Uni {
+  /**
+   * 监听用户主动截屏事件,用户使用系统截屏按键截屏时触发此事件。
+   *
+   * 文档: [https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen](https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen)
+   */
+  onUserCaptureScreen(callback: UniNamespace.OnUserCaptureScreenCallback): void;
+  /**
+   * 用户主动截屏事件。取消事件监听。
+   *
+   * 文档: [https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen](https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen)
+   */
+  offUserCaptureScreen(callback: UniNamespace.OnUserCaptureScreenCallback): void;
+}

+ 92 - 0
uni_modules/uni-usercapturescreen/package.json

@@ -0,0 +1,92 @@
+{
+	"id": "uni-usercapturescreen",
+	"displayName": "uni-usercapturescreen",
+	"version": "1.0.4",
+	"description": "用户主动截屏事件监听",
+	"keywords": [
+        "截屏"
+    ],
+	"repository": "",
+	"engines": {
+		"HBuilderX": "^3.7.7"
+	},
+	"dcloudext": {
+		"type": "uts",
+		"sale": {
+			"regular": {
+				"price": "0.00"
+			},
+			"sourcecode": {
+				"price": "0.00"
+			}
+		},
+		"contact": {
+			"qq": ""
+		},
+		"declaration": {
+			"ads": "无",
+			"data": "插件不采集任何数据",
+			"permissions": "无"
+		},
+		"npmurl": ""
+	},
+	"uni_modules": {
+		"uni-ext-api":{
+			"uni": {
+				"onUserCaptureScreen": "onUserCaptureScreen",
+				"offUserCaptureScreen": "offUserCaptureScreen",
+				"setUserCaptureScreen": "setUserCaptureScreen"
+			}
+		},
+		"dependencies": [],
+		"encrypt": [],
+		"platforms": {
+			"cloud": {
+				"tcb": "y",
+				"aliyun": "y"
+			},
+			"client": {
+				"Vue": {
+					"vue2": "n",
+					"vue3": "y"
+				},
+				"App": {
+					"app-android": {
+                        "minVersion": "19"
+                    },
+					"app-ios": {
+                        "minVersion": "9"
+                    }
+				},
+				"H5-mobile": {
+					"Safari": "n",
+					"Android Browser": "n",
+					"微信浏览器(Android)": "n",
+					"QQ浏览器(Android)": "n"
+				},
+				"H5-pc": {
+					"Chrome": "n",
+					"IE": "n",
+					"Edge": "n",
+					"Firefox": "n",
+					"Safari": "n"
+				},
+				"小程序": {
+					"微信": "y",
+					"阿里": "n",
+					"百度": "n",
+					"字节跳动": "n",
+					"QQ": "n",
+					"钉钉": "n",
+					"快手": "n",
+					"飞书": "n",
+					"京东": "n"
+				},
+				"快应用": {
+					"华为": "n",
+					"联盟": "n"
+				}
+			}
+		}
+	}
+}

+ 21 - 0
uni_modules/uni-usercapturescreen/readme.md

@@ -0,0 +1,21 @@
+# uni-usercapturescreen
+
+用户主动截屏事件监听
+
+### uni.onUserCaptureScreen
+
+监听用户主动截屏事件,用户使用系统截屏按键截屏时触发此事件。
+
+> 使用文档:[https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen](https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen)
+
+### uni.offUserCaptureScreen
+
+用户主动截屏事件。取消事件监听。
+
+> 使用文档:[https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen](https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen)
+
+### uni.setUserCaptureScreen
+
+开启/关闭防截屏。
+
+> 使用文档:[https://uniapp.dcloud.net.cn/api/system/capture-screen.html#setusercapturescreen](https://uniapp.dcloud.net.cn/api/system/capture-screen.html#setusercapturescreen)

+ 3 - 0
uni_modules/uni-usercapturescreen/utssdk/app-android/config.json

@@ -0,0 +1,3 @@
+{
+  "minSdkVersion": "19"
+}

+ 139 - 0
uni_modules/uni-usercapturescreen/utssdk/app-android/index.uts

@@ -0,0 +1,139 @@
+import { UTSAndroid } from "io.dcloud.uts";
+import ActivityCompat from "androidx.core.app.ActivityCompat";
+import Manifest from "android.Manifest";
+import PackageManager from "android.content.pm.PackageManager";
+import Build from "android.os.Build";
+import FileObserver from "android.os.FileObserver";
+import File from "java.io.File";
+import Environment from "android.os.Environment";
+import System from 'java.lang.System';
+import WindowManager from 'android.view.WindowManager';
+import { OnUserCaptureScreenCallbackResult, UserCaptureScreenCallback, OnUserCaptureScreen, OffUserCaptureScreen, SetUserCaptureScreenSuccess, SetUserCaptureScreenOptions, SetUserCaptureScreen } from "../interface.uts";
+
+
+/**
+	* 文件监听器
+	*/
+let observer : ScreenFileObserver | null = null;
+/**
+	* 记录文件监听器上次监听的时间戳,避免重复监听
+	*/
+let lastObserverTime : number = 0;
+/**
+	* 截屏回调
+	*/
+let listener : UserCaptureScreenCallback | null = null;
+
+/**
+	* android 文件监听实现
+	*/
+class ScreenFileObserver extends FileObserver {
+
+	/**
+		* 截屏文件目录
+		*/
+	private screenFile : File;
+
+	constructor(screenFile : File) {
+		super(screenFile);
+		this.screenFile = screenFile;
+	}
+
+	override onEvent(event : Int, path : string | null) : void {
+		// 只监听文件新增事件
+		if (event == FileObserver.CREATE) {
+			if (path != null) {
+				const currentTime = System.currentTimeMillis();
+				if ((currentTime - lastObserverTime) < 1000) {
+					// 本地截屏行为比上一次超过1000ms, 才认为是一个有效的时间
+					return;
+				}
+				lastObserverTime = currentTime;
+
+				const screenShotPath = new File(this.screenFile, path).getPath();
+				const res : OnUserCaptureScreenCallbackResult = {
+					path: screenShotPath
+				}
+				listener?.(res);
+			}
+		}
+	}
+}
+
+/**
+	* 开启截图监听
+	*/
+export const onUserCaptureScreen : OnUserCaptureScreen = function (callback : UserCaptureScreenCallback | null) {
+	// 检查相关权限是否已授予
+	if (ActivityCompat.checkSelfPermission(UTSAndroid.getAppContext()!, Manifest.permission.READ_EXTERNAL_STORAGE) != PackageManager.PERMISSION_GRANTED) {
+		// 无权限,申请权限
+		ActivityCompat.requestPermissions(UTSAndroid.getUniActivity()!, arrayOf(Manifest.permission.READ_EXTERNAL_STORAGE), 1001);
+		return;
+	}
+	// 更新监听
+	listener = callback;
+
+	let directory_screenshot : File;
+	if (Build.MANUFACTURER.toLowerCase() == "xiaomi") {
+		// @Suppress("DEPRECATION")
+		directory_screenshot = new File(new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM), "Screenshots");
+	} else {
+		// @Suppress("DEPRECATION")
+		directory_screenshot = new File(new File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES), "Screenshots");
+	}
+	// 先结束监听 再开启监听
+	observer?.stopWatching();
+	observer = new ScreenFileObserver(directory_screenshot);
+	observer?.startWatching();
+	
+	
+	UTSAndroid.onAppActivityDestroy(function(){
+		observer?.stopWatching()
+		observer = null
+	})
+	
+	
+}
+
+/**
+	* 关闭截屏监听
+	*/
+export const offUserCaptureScreen : OffUserCaptureScreen = function (_ : UserCaptureScreenCallback | null) {
+	// android10以上,关闭监听通过移除文件监听器实现
+	observer?.stopWatching();
+	observer = null;
+	lastObserverTime = 0;
+}
+
+/**
+	* 设置是否禁止截屏
+	*/
+export const setUserCaptureScreen : SetUserCaptureScreen = function (option : SetUserCaptureScreenOptions) {
+	// 切换到UI线程
+	UTSAndroid.getUniActivity()?.runOnUiThread(new SetUserCaptureScreenRunnable(option.enable));
+	const res : SetUserCaptureScreenSuccess = {}
+	option.success?.(res);
+	option.complete?.(res);
+}
+
+class SetUserCaptureScreenRunnable extends Runnable {
+
+	/**
+		* ture: 允许用户截屏
+		* false: 不允许用户截屏,防止用户截屏到应用页面内容
+		*/
+	private enable : boolean;
+
+	constructor(enable : boolean) {
+		super();
+		this.enable = enable;
+	}
+
+	override run() : void {
+		if (this.enable) {
+			UTSAndroid.getUniActivity()?.getWindow()?.clearFlags(WindowManager.LayoutParams.FLAG_SECURE);
+		} else {
+			UTSAndroid.getUniActivity()?.getWindow()?.addFlags(WindowManager.LayoutParams.FLAG_SECURE);
+		}
+	}
+}

+ 3 - 0
uni_modules/uni-usercapturescreen/utssdk/app-ios/config.json

@@ -0,0 +1,3 @@
+{
+  "deploymentTarget": "9"
+}

+ 146 - 0
uni_modules/uni-usercapturescreen/utssdk/app-ios/index.uts

@@ -0,0 +1,146 @@
+import { NotificationCenter } from 'Foundation';
+import { CGRect } from "CoreFoundation";
+import { UIApplication, UIView, UITextField, UIScreen, UIDevice } from "UIKit"
+import { UTSiOS } from "DCloudUTSFoundation"
+import { DispatchQueue } from 'Dispatch';
+import { SetUserCaptureScreenOptions, OnUserCaptureScreenCallbackResult, OnUserCaptureScreen, OffUserCaptureScreen, SetUserCaptureScreen, UserCaptureScreenCallback, SetUserCaptureScreenSuccess, SetUserCaptureScreenFail  } from "../interface.uts"
+
+/**
+	* 定义监听截屏事件工具类
+	*/
+class CaptureScreenTool {
+	static listener : UserCaptureScreenCallback | null;
+	static secureView : UIView | null;
+
+	// 监听截屏
+	static listenCaptureScreen(callback : UserCaptureScreenCallback | null) {
+		this.listener = callback
+ 
+		// 注册监听截屏事件及回调方法
+		// target-action 回调方法需要通过 Selector("方法名") 构建
+		const method = Selector("userDidTakeScreenshot")
+		NotificationCenter.default.addObserver(this, selector = method, name = UIApplication.userDidTakeScreenshotNotification, object = null)
+	}
+
+	// 捕获截屏回调的方法
+	// target-action 的方法前需要添加 @objc 前缀
+	@objc static userDidTakeScreenshot() {
+		// 回调
+		const res: OnUserCaptureScreenCallbackResult = {
+		}
+		this.listener?.(res)
+	}
+
+	// 移除监听事件
+	static removeListen(callback : UserCaptureScreenCallback | null) {
+		this.listener = null
+		NotificationCenter.default.removeObserver(this)
+	}
+
+	static createSecureView() : UIView | null {
+		let field = new UITextField(frame = CGRect.zero)
+		field.isSecureTextEntry = true
+		if (field.subviews.length > 0 && UIDevice.current.systemVersion != '15.1') {
+			let view = field.subviews[0]
+			view.subviews.forEach((item) => {
+				item.removeFromSuperview()
+			})
+			view.isUserInteractionEnabled = true
+			return view
+		}
+		return null
+	}
+
+	// 开启防截屏
+	static onAntiScreenshot(option : SetUserCaptureScreenOptions) {
+		// uts方法默认会在子线程中执行,涉及 UI 操作必须在主线程中运行,通过 DispatchQueue.main.async 方法可将代码在主线程中运行
+		DispatchQueue.main.async(execute = () : void => {
+			let secureView = this.createSecureView()
+			let window = UTSiOS.getKeyWindow()
+			let rootView = window.rootViewController == null ? null : window.rootViewController!.view
+			if (secureView != null && rootView != null) {
+				let rootSuperview = rootView!.superview
+				if (rootSuperview != null) {
+					this.secureView = secureView
+					rootSuperview!.addSubview(secureView!)
+					rootView!.removeFromSuperview()
+					secureView!.addSubview(rootView!)
+					let rect = rootView!.frame
+					secureView!.frame = UIScreen.main.bounds
+					rootView!.frame = rect
+				}
+			}
+			let res: SetUserCaptureScreenSuccess = {
+			}
+			option.success?.(res)
+			option.complete?.(res)
+		})
+	}
+
+	// 关闭防截屏
+	static offAntiScreenshot(option : SetUserCaptureScreenOptions) {
+		DispatchQueue.main.async(execute = () : void => {
+			if (this.secureView != null) {
+				let window = UTSiOS.getKeyWindow()
+				let rootView = window.rootViewController == null ? null : window.rootViewController!.view
+				if (rootView != null && this.secureView!.superview != null) {
+					let rootSuperview = this.secureView!.superview
+					if (rootSuperview != null) {
+						rootSuperview!.addSubview(rootView!)
+						this.secureView!.removeFromSuperview()
+					}
+				}
+				this.secureView = null
+			}
+			let res: SetUserCaptureScreenSuccess = {
+			}
+			option.success?.(res)
+			option.complete?.(res)
+		})
+	}
+}
+
+/**
+	* 开启截图监听
+	*/
+export const onUserCaptureScreen : OnUserCaptureScreen = function (callback : UserCaptureScreenCallback | null) {
+	CaptureScreenTool.listenCaptureScreen(callback)
+}
+
+/**
+	* 关闭截屏监听
+	*/
+export const offUserCaptureScreen : OffUserCaptureScreen = function (callback : UserCaptureScreenCallback | null) {
+	CaptureScreenTool.removeListen(callback)
+}
+
+/**
+	* 开启/关闭防截屏
+	*/
+export const setUserCaptureScreen : SetUserCaptureScreen = function (options : SetUserCaptureScreenOptions) {
+	if (UIDevice.current.systemVersion < "13.0") {
+		let res: SetUserCaptureScreenFail = {
+			errCode: 12001,
+			errSubject: "uni-usercapturescreen",
+			errMsg: "setUserCaptureScreen:system not support"
+		}
+		options.fail?.(res);
+		options.complete?.(res);
+
+	} else if (UIDevice.current.systemVersion == "15.1") {
+		let res: SetUserCaptureScreenFail = {
+			errCode: 12010,
+			errSubject: "uni-usercapturescreen",
+			errMsg: "setUserCaptureScreen:system internal error"
+		}
+		options.fail?.(res);
+		options.complete?.(res);
+	} else {
+		if (options.enable == true) {
+			CaptureScreenTool.offAntiScreenshot(options)
+		} else {
+			CaptureScreenTool.onAntiScreenshot(options)
+		}
+	}
+}
+

+ 122 - 0
uni_modules/uni-usercapturescreen/utssdk/interface.uts

@@ -0,0 +1,122 @@
+/**
+	* uni.onUserCaptureScreen/uni.offUserCaptureScreen回调参数
+	*/
+export type OnUserCaptureScreenCallbackResult = {
+	/**
+		* 截屏文件路径(仅Android返回)
+		*/
+	path ?: string
+}
+
+/**
+	* uni.onUserCaptureScreen/uni.offUserCaptureScreen回调函数定义
+	*/
+export type UserCaptureScreenCallback = (res : OnUserCaptureScreenCallbackResult) => void
+
+/**
+	* uni.onUserCaptureScreen函数定义
+	* 开启截屏监听 
+	* 
+	* @param {UserCaptureScreenCallback} callback 
+	* @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen
+	* @platforms APP-IOS = ^9.0,APP-ANDROID = ^4.4
+	* @since 3.7.7
+	*/
+export type OnUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void
+
+/**
+	* uni.offUserCaptureScreen函数定义
+	* 关闭截屏监听  
+	* 
+	* @param {UserCaptureScreenCallback} callback 
+	* @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen
+	* @platforms APP-IOS = ^9.0,APP-ANDROID = ^4.4
+	* @since 3.7.7
+	*/
+export type OffUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void
+
+/**
+	* uni.setUserCaptureScreen成功回调参数
+	*/
+export type SetUserCaptureScreenSuccess = {
+}
+
+/**
+	* uni.setUserCaptureScreen失败回调参数
+	*/
+export type SetUserCaptureScreenFail = {
+	/**
+		* 错误码
+		* 12001:system not support
+		* 12010:system internal error
+		*/
+	errCode : number,
+	/**
+		* 调用API的名称
+		*/
+	errSubject : string,
+	/**
+		* 错误的详细信息
+		*/
+	errMsg : string,
+}
+
+/**
+	* uni.setUserCaptureScreen成功回调函数定义
+	*/
+export type SetUserCaptureScreenSuccessCallback = (res : SetUserCaptureScreenSuccess) => void
+
+/**
+	* uni.setUserCaptureScreen失败回调函数定义
+	*/
+export type SetUserCaptureScreenFailCallback = (res : SetUserCaptureScreenFail) => void
+
+/**
+	* uni.setUserCaptureScreen完成回调函数定义
+	*/
+export type SetUserCaptureScreenCompleteCallback = (res : any) => void
+
+/**
+	* uni.setUserCaptureScreen参数
+	*/
+
+export type SetUserCaptureScreenOptions = {
+	/**
+	* true: 允许用户截屏 false: 不允许用户截屏,防止用户截屏到应用页面内容
+	*/
+	enable : boolean;
+	/**
+	* 接口调用成功的回调函数
+	*/
+	// success : SetUserCaptureScreenSuccessCallback | null,
+	success ?: SetUserCaptureScreenSuccessCallback,
+	/**
+	* 接口调用失败的回调函数
+	*/
+	// fail : SetUserCaptureScreenFailCallback | null,
+	fail ?: SetUserCaptureScreenFailCallback,
+	/**
+	* 接口调用结束的回调函数(调用成功、失败都会执行)
+	*/
+	// complete : SetUserCaptureScreenSuccessCallback | SetUserCaptureScreenFailCallback | null
+	complete ?: SetUserCaptureScreenCompleteCallback
+}
+
+
+/**
+ * * uni.setUserCaptureScreen函数定义
+
+	* 设置防截屏  
+	* 
+	* @param {SetUserCaptureScreenOptions} options 
+	* @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#setusercapturescreen
+	* @platforms APP-IOS = ^13.0,APP-ANDROID = ^4.4
+	* @since 3.7.7
+	*/
+export type SetUserCaptureScreen = (options : SetUserCaptureScreenOptions) => void
+
+export interface Uni {
+	onUserCaptureScreen : OnUserCaptureScreen,
+	offUserCaptureScreen : OffUserCaptureScreen,
+	setUserCaptureScreen : SetUserCaptureScreen
+}

+ 7 - 0
uni_modules/uni-usercapturescreen/utssdk/mp-weixin/index.js

@@ -0,0 +1,7 @@
+export function onUserCaptureScreen (callback) {
+  return wx.onUserCaptureScreen(callback)
+}
+
+export function offUserCaptureScreen (callback) {
+  return wx.offUserCaptureScreen(callback)
+}