From bad93a3a89e8300f6fdf1ed6bf4b57a3b892f92e Mon Sep 17 00:00:00 2001 From: chenghuan Date: Tue, 9 Jun 2026 16:33:15 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E8=A7=86=E9=A2=91?= =?UTF-8?q?=E6=B0=B4=E5=8D=B0=E5=92=8C=E9=98=B2=E7=9B=97=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- config/baseUrl.js | 4 +- manifest.json | 4 +- package-lock.json | 6 +- package.json | 2 +- pages/curriculum/order/detail.vue | 1 + .../uni-usercapturescreen/changelog.md | 14 ++ uni_modules/uni-usercapturescreen/index.d.ts | 157 ++++++++++++++++ .../uni-usercapturescreen/package.json | 97 ++++++++++ uni_modules/uni-usercapturescreen/readme.md | 21 +++ .../utssdk/app-android/index.uts | 139 ++++++++++++++ .../utssdk/app-harmony/index.uts | 87 +++++++++ .../utssdk/app-ios/index.uts | 141 +++++++++++++++ .../utssdk/interface.uts | 170 ++++++++++++++++++ .../utssdk/mp-weixin/index.js | 7 + .../uni-usercapturescreen/utssdk/unierror.uts | 35 ++++ 15 files changed, 877 insertions(+), 8 deletions(-) create mode 100644 uni_modules/uni-usercapturescreen/changelog.md create mode 100644 uni_modules/uni-usercapturescreen/index.d.ts create mode 100644 uni_modules/uni-usercapturescreen/package.json create mode 100644 uni_modules/uni-usercapturescreen/readme.md create mode 100644 uni_modules/uni-usercapturescreen/utssdk/app-android/index.uts create mode 100644 uni_modules/uni-usercapturescreen/utssdk/app-harmony/index.uts create mode 100644 uni_modules/uni-usercapturescreen/utssdk/app-ios/index.uts create mode 100644 uni_modules/uni-usercapturescreen/utssdk/interface.uts create mode 100644 uni_modules/uni-usercapturescreen/utssdk/mp-weixin/index.js create mode 100644 uni_modules/uni-usercapturescreen/utssdk/unierror.uts diff --git a/config/baseUrl.js b/config/baseUrl.js index c67c7af..36898be 100644 --- a/config/baseUrl.js +++ b/config/baseUrl.js @@ -3,8 +3,8 @@ let socketUrl = ""; if (process.env.NODE_ENV === 'development') { // 开发环境 // baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川 - // baseUrl = "https://api.nuttyreading.com/"; //线上正式 - baseUrl = "http://192.168.110.131:9200/pb/"; // 王亚男 + baseUrl = "https://api.nuttyreading.com/"; //线上正式 + // baseUrl = "http://192.168.110.131:9200/pb/"; // 王亚男 } else if (process.env.NODE_ENV === 'production') { // baseUrl = "http://192.168.110.100:9200/pb/"; // 张川川 baseUrl = "https://api.nuttyreading.com/"; //线上正式 diff --git a/manifest.json b/manifest.json index 1bffb29..3f9a384 100644 --- a/manifest.json +++ b/manifest.json @@ -2,9 +2,9 @@ "name" : "心灵空间", "appid" : "__UNI__BBBDFD2", "description" : "心灵空间", - "versionName" : "1.0.60", + "versionName" : "1.0.61", "sassImplementationName" : "node-sass", - "versionCode" : 1060, + "versionCode" : 1061, "transformPx" : false, /* 5+App特有相关 */ "app-plus" : { diff --git a/package-lock.json b/package-lock.json index e650896..1509550 100644 --- a/package-lock.json +++ b/package-lock.json @@ -9,7 +9,7 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13", + "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.14", "jquery": "^3.7.1", "tcplayer.js": "^5.1.0" }, @@ -68,8 +68,8 @@ "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" }, "node_modules/edu-core": { - "version": "1.0.13", - "resolved": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#b5ece8b8abfeba98428f6e2191efae3312b5036f", + "version": "1.0.14", + "resolved": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#eb9eccdc3d281fe84127a93e019650512859ff0c", "license": "ISC" }, "node_modules/es5-shim": { diff --git a/package.json b/package.json index 04b78f2..299ed42 100644 --- a/package.json +++ b/package.json @@ -14,7 +14,7 @@ }, "homepage": "https://github.com/dcloudio/hello-uniapp#readme", "dependencies": { - "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.13", + "edu-core": "git+https://git.nuttyreading.com/chenghuan/edu-core.git#v1.0.14", "jquery": "^3.7.1", "tcplayer.js": "^5.1.0" }, diff --git a/pages/curriculum/order/detail.vue b/pages/curriculum/order/detail.vue index db5bc7d..54dc035 100644 --- a/pages/curriculum/order/detail.vue +++ b/pages/curriculum/order/detail.vue @@ -15,6 +15,7 @@ :course="{courseTitle:options.navTitle, chapterTitle: curriculumData.title, catalogueId: curriculumData.catalogueId || '', courseId: curriculumData.courseId || ''}" :cover="options.curriculumImgUrl" :http="$http" + :user-info="userInfo" /> void + + type OnUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void + + type OffUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void + + /** + * uni.setUserCaptureScreen成功回调参数 + */ + type SetUserCaptureScreenSuccess = { + + } + + /** + * uni.setUserCaptureScreen成功回调函数定义 + */ + type SetUserCaptureScreenSuccessCallback = (res : SetUserCaptureScreenSuccess) => void + + /** + * 错误码 + * - 12001 "setUserCaptureScreen:system not support" + * - 12010 "setUserCaptureScreen:system internal error" + */ + type SetUserCaptureScreenErrorCode = 12001 | 12010; + + /** + * SetUserCaptureScreen 的错误回调参数 + */ + interface SetUserCaptureScreenFail { + errCode : SetUserCaptureScreenErrorCode + } + + /** + * uni.setUserCaptureScreen失败回调函数定义 + */ + type SetUserCaptureScreenFailCallback = (res : SetUserCaptureScreenFail) => void + + /** + * uni.setUserCaptureScreen完成回调函数定义 + */ + type SetUserCaptureScreenCompleteCallback = (res : any) => void + + /** + * uni.setUserCaptureScreen参数 + */ + type SetUserCaptureScreenOptions = { + /** + * true: 允许用户截屏 false: 不允许用户截屏,防止用户截屏到应用页面内容 + */ + enable : boolean; + /** + * 接口调用成功的回调函数 + */ + // success : SetUserCaptureScreenSuccessCallback | null, + success ?: SetUserCaptureScreenSuccessCallback, + /** + * 接口调用失败的回调函数 + */ + // fail : SetUserCaptureScreenFailCallback | null, + fail ?: SetUserCaptureScreenFailCallback, + /** + * 接口调用结束的回调函数(调用成功、失败都会执行) + */ + // complete : SetUserCaptureScreenSuccessCallback | SetUserCaptureScreenFailCallback | null + complete ?: SetUserCaptureScreenCompleteCallback + } + + type SetUserCaptureScreen = (options : SetUserCaptureScreenOptions) => void +} + +declare interface Uni { + /** + * 开启截屏监听 + * + * @param {UserCaptureScreenCallback} callback + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "3.7.7", + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + * @autotest { expectCallback: true } + */ + onUserCaptureScreen(callback : UniNamespace.UserCaptureScreenCallback | null) : void, + /** + * 关闭截屏监听 + * + * @param {UserCaptureScreenCallback} callback + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "9.0", + * "uniVer": "3.7.7", + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + * @autotest { expectCallback: true } + */ + offUserCaptureScreen(callback : UniNamespace.UserCaptureScreenCallback | null) : void, + /** + * 设置防截屏 + * + * @param {SetUserCaptureScreenOptions} options + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#setusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "13.0", + * "uniVer": "3.7.7", + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + */ + setUserCaptureScreen(options : UniNamespace.SetUserCaptureScreenOptions) : void +} diff --git a/uni_modules/uni-usercapturescreen/package.json b/uni_modules/uni-usercapturescreen/package.json new file mode 100644 index 0000000..d27fbd6 --- /dev/null +++ b/uni_modules/uni-usercapturescreen/package.json @@ -0,0 +1,97 @@ +{ + "id": "uni-usercapturescreen", + "displayName": "uni-usercapturescreen", + "version": "1.0.6", + "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": { + "web": false + }, + "offUserCaptureScreen": { + "web": false + }, + "setUserCaptureScreen": { + "web": false, + "mp-weixin": false + } + } + }, + "dependencies": [], + "encrypt": [], + "platforms": { + "cloud": { + "tcb": "y", + "aliyun": "y", + "alipay": "n" + }, + "client": { + "Vue": { + "vue2": "y", + "vue3": "y" + }, + "App": { + "app-android": "y", + "app-ios": "y", + "app-harmony": "y" + }, + "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" + } + } + } + } +} diff --git a/uni_modules/uni-usercapturescreen/readme.md b/uni_modules/uni-usercapturescreen/readme.md new file mode 100644 index 0000000..eda987a --- /dev/null +++ b/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) diff --git a/uni_modules/uni-usercapturescreen/utssdk/app-android/index.uts b/uni_modules/uni-usercapturescreen/utssdk/app-android/index.uts new file mode 100644 index 0000000..c5c1bd4 --- /dev/null +++ b/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"; +import string from 'android.R.string'; + + +/** + * 文件监听器 + */ +let observer : ScreenFileObserver | null = null; +/** + * 记录文件监听器上次监听的时间戳,避免重复监听 + */ +let lastObserverTime : number = 0; +/** + * 截屏回调 + */ +let listener : UserCaptureScreenCallback | null = null; + +/** + * android 文件监听实现 + */ +class ScreenFileObserver extends FileObserver { + + /** + * 截屏文件目录 + */ + private screenFile : File; + + constructor(screenFileStr : string) { + super(screenFileStr); + this.screenFile = new File(screenFileStr); + } + + 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.getPath()); + 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); + } + } +} diff --git a/uni_modules/uni-usercapturescreen/utssdk/app-harmony/index.uts b/uni_modules/uni-usercapturescreen/utssdk/app-harmony/index.uts new file mode 100644 index 0000000..0eeb40d --- /dev/null +++ b/uni_modules/uni-usercapturescreen/utssdk/app-harmony/index.uts @@ -0,0 +1,87 @@ +import { display } from '@kit.ArkUI'; +import { window } from '@kit.ArkUI'; +import { Callback, BusinessError } from '@kit.BasicServicesKit'; + +import { getAbilityContext } from '@dcloudio/uni-runtime'; + +import { + OnUserCaptureScreen, UserCaptureScreenCallback, + OffUserCaptureScreen, + SetUserCaptureScreen, SetUserCaptureScreenOptions, SetUserCaptureScreenSuccess +} from '../interface.uts'; + +const onUserCaptureScreenCallbacks: Function[] = [] + +const harmonyCaptureStatusChange: Callback = (captureStatus: boolean) => { + if (captureStatus) { + onUserCaptureScreenCallbacks.forEach(cb => { + typeof cb === 'function' && cb() + }) + } +} + +display.on('captureStatusChange', harmonyCaptureStatusChange) + +export const onUserCaptureScreen: OnUserCaptureScreen = function (callback: UserCaptureScreenCallback | null) { + if (callback) { + onUserCaptureScreenCallbacks.push(callback) + } +} + +export const offUserCaptureScreen: OffUserCaptureScreen = function (callback: UserCaptureScreenCallback | null) { + if (callback) { + const index = onUserCaptureScreenCallbacks.indexOf(callback) + if (index > -1) { + onUserCaptureScreenCallbacks.splice(index, 1) + } + } +} + +export const setUserCaptureScreen: SetUserCaptureScreen = function (options: SetUserCaptureScreenOptions) { + const errSubject = 'uni-usercapturescreen' + const setUserCaptureScreenSuccess: SetUserCaptureScreenSuccess = {} + window.getLastWindow(getAbilityContext()!, (err, window) => { + const errCode: number = err.code; + if (errCode) { + options.fail?.({ + errCode: (err as BusinessError).code, + errSubject, + errMsg: `setUserCaptureScreen:fail ${(err as BusinessError).message}` + } as IUniError) + options.complete?.(setUserCaptureScreenSuccess); + return; + } else { + try { + UTSHarmony.requestSystemPermission(['ohos.permission.PRIVACY_WINDOW'], (allRight: boolean, _grantedList: string[]) => { + if (allRight) { + window.setWindowPrivacyMode(!options.enable, (err: BusinessError) => { + const errCode: number = err.code; + if (errCode) { + options.fail?.({ + errCode: err.code, + errSubject, + errMsg: `setUserCaptureScreen:fail ${err.message}` + } as IUniError) + options.complete?.(setUserCaptureScreenSuccess); + return; + } + options.success?.(setUserCaptureScreenSuccess); + options.complete?.(setUserCaptureScreenSuccess); + }); + } else { + throw new Error('permission denied') + } + }, (_doNotAskAgain: boolean, _grantedList: string[]) => { + throw new Error('permission denied'); + }) + } catch (err) { + options.fail?.({ + errCode: (err as BusinessError).code, + errSubject, + errMsg: `setUserCaptureScreen:fail ${(err as BusinessError).message}` + } as IUniError) + options.complete?.(setUserCaptureScreenSuccess); + } + } + }) +} diff --git a/uni_modules/uni-usercapturescreen/utssdk/app-ios/index.uts b/uni_modules/uni-usercapturescreen/utssdk/app-ios/index.uts new file mode 100644 index 0000000..17eec4b --- /dev/null +++ b/uni_modules/uni-usercapturescreen/utssdk/app-ios/index.uts @@ -0,0 +1,141 @@ +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 } from "../interface.uts" +import { SetUserCaptureScreenFailImpl } from "../unierror.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!) + // secureView 充满父视图并随父视图宽高自适应(横竖屏、全屏切换时自动跟随) + secureView!.frame = rootSuperview!.bounds + secureView!.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight] + // rootView 充满 secureView 并自适应,避免全屏时被固定在竖屏尺寸而缩到角落 + rootView!.frame = secureView!.bounds + rootView!.autoresizingMask = [UIView.AutoresizingMask.flexibleWidth, UIView.AutoresizingMask.flexibleHeight] + } + } + 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 = new SetUserCaptureScreenFailImpl(12001) + options.fail?.(res); + options.complete?.(res); + + } else if (UIDevice.current.systemVersion == "15.1") { + let res = new SetUserCaptureScreenFailImpl(12010) + options.fail?.(res); + options.complete?.(res); + } else { + if (options.enable == true) { + CaptureScreenTool.offAntiScreenshot(options) + } else { + CaptureScreenTool.onAntiScreenshot(options) + } + } +} + diff --git a/uni_modules/uni-usercapturescreen/utssdk/interface.uts b/uni_modules/uni-usercapturescreen/utssdk/interface.uts new file mode 100644 index 0000000..b4abf42 --- /dev/null +++ b/uni_modules/uni-usercapturescreen/utssdk/interface.uts @@ -0,0 +1,170 @@ +/** + * uni.onUserCaptureScreen/uni.offUserCaptureScreen回调参数 + */ + export type OnUserCaptureScreenCallbackResult = { + /** + * 截屏文件路径(仅Android返回) + */ + path ?: string + } + + /** + * uni.onUserCaptureScreen/uni.offUserCaptureScreen回调函数定义 + */ + export type UserCaptureScreenCallback = (res : OnUserCaptureScreenCallbackResult) => void + + export type OnUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void + + export type OffUserCaptureScreen = (callback : UserCaptureScreenCallback | null) => void + + /** + * uni.setUserCaptureScreen成功回调参数 + */ + export type SetUserCaptureScreenSuccess = { + } + + + /** + * uni.setUserCaptureScreen成功回调函数定义 + */ + export type SetUserCaptureScreenSuccessCallback = (res : SetUserCaptureScreenSuccess) => void + + /** + * uni.setUserCaptureScreen失败回调函数定义 + */ + export type SetUserCaptureScreenFailCallback = (res : IUniError) => 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 + } + + /** + * 错误码 + * - 12001 "setUserCaptureScreen:system not support" + * - 12010 "setUserCaptureScreen:system internal error" + */ + export type SetUserCaptureScreenErrorCode = 12001 | 12010; + /** + * SetUserCaptureScreen 的错误回调参数 + */ + export interface SetUserCaptureScreenFail extends IUniError { + errCode : SetUserCaptureScreenErrorCode + }; + + export type SetUserCaptureScreen = (options : SetUserCaptureScreenOptions) => void + + export interface Uni { + /** + * 开启截屏监听 + * + * @param {UserCaptureScreenCallback} callback + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#onusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "12.0", + * "uniVer": "3.7.7", + * "unixVer": "4.11" + * }, + * "harmony": { + * "osVer": "3.0", + * "uniVer": "4.25", + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + * @autotest { expectCallback: true } + */ + onUserCaptureScreen(callback : UserCaptureScreenCallback | null) : void, + /** + * 关闭截屏监听 + * + * @param {UserCaptureScreenCallback} callback + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#offusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "12.0", + * "uniVer": "3.7.7", + * "unixVer": "4.11" + * }, + * "harmony": { + * "osVer": "3.0", + * "uniVer": "4.25",, + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + * @autotest { expectCallback: true } + */ + offUserCaptureScreen(callback : UserCaptureScreenCallback | null) : void, + /** + * 设置防截屏 + * + * @param {SetUserCaptureScreenOptions} options + * @tutorial https://uniapp.dcloud.net.cn/api/system/capture-screen.html#setusercapturescreen + * @uniPlatform { + * "app": { + * "android": { + * "osVer": "4.4.4", + * "uniVer": "3.7.7", + * "unixVer": "3.9.0" + * }, + * "ios": { + * "osVer": "13.0", + * "uniVer": "3.7.7", + * "unixVer": "4.11" + * }, + * "harmony": { + * "osVer": "3.0", + * "uniVer": "4.25", + * "unixVer": "x" + * } + * } + * } + * @uniVersion 3.7.7 + * @uniVueVersion 2,3 //支持的vue版本 + */ + setUserCaptureScreen(options : SetUserCaptureScreenOptions) : void + } diff --git a/uni_modules/uni-usercapturescreen/utssdk/mp-weixin/index.js b/uni_modules/uni-usercapturescreen/utssdk/mp-weixin/index.js new file mode 100644 index 0000000..6aa57b3 --- /dev/null +++ b/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) +} diff --git a/uni_modules/uni-usercapturescreen/utssdk/unierror.uts b/uni_modules/uni-usercapturescreen/utssdk/unierror.uts new file mode 100644 index 0000000..0f359a8 --- /dev/null +++ b/uni_modules/uni-usercapturescreen/utssdk/unierror.uts @@ -0,0 +1,35 @@ +import { SetUserCaptureScreenErrorCode, SetUserCaptureScreenFail } from "./interface.uts" +/** + * 错误主题 + */ +export const UniErrorSubject = 'uni-usercapturescreen'; + + +/** + * 错误信息 + * @UniError + */ +export const UniErrors : Map = new Map([ + /** + * 错误码及对应的错误信息 + */ + [12001, 'setUserCaptureScreen:system not support'], + [12010, 'setUserCaptureScreen:system internal error'], +]); + + +/** + * 错误对象实现 + */ +export class SetUserCaptureScreenFailImpl extends UniError implements SetUserCaptureScreenFail { + + /** + * 错误对象构造函数 + */ + constructor(errCode : SetUserCaptureScreenErrorCode) { + super(); + this.errSubject = UniErrorSubject; + this.errCode = errCode; + this.errMsg = UniErrors[errCode] ?? ""; + } +} \ No newline at end of file