提交 4ab45d79 编写于 作者: 杜庆泉's avatar 杜庆泉

修复android 10一下截屏监听不灵的bug

上级 faf124f0
{
"version": "1",
"prompt": "template",
"title": "服务协议和隐私政策",
"message": "  请你务必审慎阅读、充分理解“服务协议”和“隐私政策”各条款,包括但不限于:为了更好的向你提供服务,我们需要收集你的设备标识、操作日志等信息用于分析、优化应用性能。<br/>  你可阅读<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>了解详细信息。如果你同意,请点击下面按钮开始接受我们的服务。",
"buttonAccept": "同意并接受",
"buttonRefuse": "暂不同意",
"hrefLoader": "system|default",
"second": {
"title": "确认提示",
"message": "  进入应用前,你需先同意<a href=\"\">《服务协议》</a>和<a href=\"\">《隐私政策》</a>,否则将退出应用。",
"buttonAccept": "同意并继续",
"buttonRefuse": "退出应用"
},
"styles": {
"backgroundColor": "#00FF00",
"borderRadius":"5px",
"title": {
"color": "#ff00ff"
},
"buttonAccept": {
"color": "#ffff00"
},
"buttonRefuse": {
"color": "#00ffff"
}
}
}
......@@ -3,64 +3,50 @@
<page-head :title="title" accordion></page-head>
<uni-collapse>
<uni-collapse accordion>
<uni-collapse-item title="延迟任务" :border="false">
<uni-list>
<uni-list-item @tap="testTimer" title="开启延迟任务" class="itemButton" :clickable="true">
<uni-list-item @tap="testTimer" title="开启延迟任务" :clickable="true">
</uni-list-item>
</uni-list>
</uni-collapse-item>
</uni-collapse>
<uni-collapse>
<uni-collapse-item title="定时任务" :border="false">
<uni-list>
<uni-list-item @tap="testInterval" title="开启定时任务" class="itemButton" :clickable="true">
<uni-list-item @tap="testInterval" title="开启定时任务" :clickable="true">
</uni-list-item>
<uni-list-item @tap="testClearInterval" title="关闭定时任务" class="itemButton" :clickable="true">
<uni-list-item @tap="testClearInterval" title="关闭定时任务" :clickable="true">
</uni-list-item>
</uni-list>
</uni-collapse-item>
</uni-collapse>
<uni-collapse>
<uni-collapse-item title="语法示例" :border="false">
<uni-list>
<uni-list-item @tap="testSyntax" title="进阶语法示例" class="itemButton" :clickable="true" link>
<uni-list-item @tap="testSyntax" title="进阶语法示例" :clickable="true" link>
</uni-list-item>
</uni-list>
</uni-collapse-item>
</uni-collapse>
<uni-collapse>
<uni-collapse-item title="资源加载示例" :border="false">
<uni-list>
<uni-list-item @tap="gotoResourceDemo" title="图片加载示例" class="itemButton" :clickable="true" link>
<uni-list-item @tap="gotoResourceDemo" title="图片加载示例" :clickable="true" link>
</uni-list-item>
</uni-list>
</uni-collapse-item>
</uni-collapse>
<uni-collapse>
<uni-collapse-item title="android平台示例" :border="false">
<uni-list>
<uni-list-item @tap="testLifecyle" title="activity生命周期监听" class="itemButton" :clickable="true" link/>
<uni-list-item title="操作DecorView" :clickable="true" @tap="gotoDecorView" link/>
<uni-list-item @tap="testAssetLoad" title="播放asset音频(需自定义基座)" class="itemButton" :clickable="true"/>
<uni-list-item @tap="testLifecyle" title="activity生命周期监听" :clickable="true" link />
<uni-list-item @tap="gotoDecorView" title="操作DecorView" :clickable="true" link />
<uni-list-item @tap="testAssetLoad" title="播放asset音频(需自定义基座)" :clickable="true" />
</uni-list>
</uni-collapse-item>
</uni-collapse>
</view>
</template>
<script>
......@@ -129,7 +115,7 @@
clearIntervalTask(this.taskId);
},
/**
* 跳转至资源加载演示界面
*/
......@@ -138,7 +124,7 @@
url: '/pages/resource/resource'
})
},
gotoDecorView : function() {
gotoDecorView: function() {
uni.navigateTo({
url: '/pages/advance/android/decorview'
})
......@@ -163,10 +149,5 @@
</script>
<style>
.itemButton {
margin-top: 5px;
margin-bottom: 5px;
margin-right: 10px;
font-size: 10px;
}
</style>
## 0.0.1(2022-07-22)
- 初始化
<template>
<view class="uni-section">
<view class="uni-section-header" @click="onClick">
<view class="uni-section-header__decoration" v-if="type" :class="type" />
<slot v-else name="decoration"></slot>
<view class="uni-section-header__content">
<text :style="{'font-size':titleFontSize,'color':titleColor}" class="uni-section__content-title" :class="{'distraction':!subTitle}">{{ title }}</text>
<text v-if="subTitle" :style="{'font-size':subTitleFontSize,'color':subTitleColor}" class="uni-section-header__content-sub">{{ subTitle }}</text>
</view>
<view class="uni-section-header__slot-right">
<slot name="right"></slot>
</view>
</view>
<view class="uni-section-content" :style="{padding: _padding}">
<slot />
</view>
</view>
</template>
<script>
/**
* Section 标题栏
* @description 标题栏
* @property {String} type = [line|circle|square] 标题装饰类型
* @value line 竖线
* @value circle 圆形
* @value square 正方形
* @property {String} title 主标题
* @property {String} titleFontSize 主标题字体大小
* @property {String} titleColor 主标题字体颜色
* @property {String} subTitle 副标题
* @property {String} subTitleFontSize 副标题字体大小
* @property {String} subTitleColor 副标题字体颜色
* @property {String} padding 默认插槽 padding
*/
export default {
name: 'UniSection',
emits:['click'],
props: {
type: {
type: String,
default: ''
},
title: {
type: String,
required: true,
default: ''
},
titleFontSize: {
type: String,
default: '14px'
},
titleColor:{
type: String,
default: '#333'
},
subTitle: {
type: String,
default: ''
},
subTitleFontSize: {
type: String,
default: '12px'
},
subTitleColor: {
type: String,
default: '#999'
},
padding: {
type: [Boolean, String],
default: false
}
},
computed:{
_padding(){
if(typeof this.padding === 'string'){
return this.padding
}
return this.padding?'10px':''
}
},
watch: {
title(newVal) {
if (uni.report && newVal !== '') {
uni.report('title', newVal)
}
}
},
methods: {
onClick() {
this.$emit('click')
}
}
}
</script>
<style lang="scss" >
$uni-primary: #2979ff !default;
.uni-section {
background-color: #fff;
.uni-section-header {
position: relative;
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: row;
align-items: center;
padding: 12px 10px;
font-weight: normal;
&__decoration{
margin-right: 6px;
background-color: $uni-primary;
&.line {
width: 4px;
height: 12px;
border-radius: 10px;
}
&.circle {
width: 8px;
height: 8px;
border-top-right-radius: 50px;
border-top-left-radius: 50px;
border-bottom-left-radius: 50px;
border-bottom-right-radius: 50px;
}
&.square {
width: 8px;
height: 8px;
}
}
&__content {
/* #ifndef APP-NVUE */
display: flex;
/* #endif */
flex-direction: column;
flex: 1;
color: #333;
.distraction {
flex-direction: row;
align-items: center;
}
&-sub {
margin-top: 2px;
}
}
&__slot-right{
font-size: 14px;
}
}
.uni-section-content{
font-size: 14px;
}
}
</style>
{
"id": "uni-section",
"displayName": "uni-section 标题栏",
"version": "0.0.1",
"description": "标题栏组件",
"keywords": [
"uni-ui",
"uniui",
"标题栏"
],
"repository": "https://github.com/dcloudio/uni-ui",
"engines": {
"HBuilderX": ""
},
"directories": {
"example": "../../temps/example_temps"
},
"dcloudext": {
"category": [
"前端组件",
"通用组件"
],
"sale": {
"regular": {
"price": "0.00"
},
"sourcecode": {
"price": "0.00"
}
},
"contact": {
"qq": ""
},
"declaration": {
"ads": "无",
"data": "无",
"permissions": "无"
},
"npmurl": "https://www.npmjs.com/package/@dcloudio/uni-ui"
},
"uni_modules": {
"dependencies": [
"uni-scss"
],
"encrypt": [],
"platforms": {
"cloud": {
"tcb": "y",
"aliyun": "y"
},
"client": {
"App": {
"app-vue": "y",
"app-nvue": "y"
},
"H5-mobile": {
"Safari": "y",
"Android Browser": "y",
"微信浏览器(Android)": "y",
"QQ浏览器(Android)": "y"
},
"H5-pc": {
"Chrome": "y",
"IE": "y",
"Edge": "y",
"Firefox": "y",
"Safari": "y"
},
"小程序": {
"微信": "y",
"阿里": "y",
"百度": "y",
"字节跳动": "y",
"QQ": "y"
},
"快应用": {
"华为": "u",
"联盟": "u"
},
"Vue": {
"vue2": "y",
"vue3": "y"
}
}
}
}
}
\ No newline at end of file
## Section 标题栏
> **组件名:uni-section**
> 代码块: `uSection`
uni-section 组件主要用于文章、列表详情等标题展示
### [查看文档](https://uniapp.dcloud.io/component/uniui/uni-section)
#### 如使用过程中有任何问题,或者您对uni-ui有一些好的建议,欢迎加入 uni-ui 交流群:871950839
......@@ -8,55 +8,24 @@ import {
import ActivityCompat from "androidx.core.app.ActivityCompat";
import Manifest from "android.Manifest";
import PackageManager from "android.content.pm.PackageManager";
import Context from "android.content.Context";
import Point from "android.graphics.Point";
import Build from "android.os.Build";
import Handler from "android.os.Handler";
import MediaStore from "android.provider.MediaStore";
import Looper from "android.os.Looper";
import Cursor from "android.database.Cursor";
import ContentObserver from "android.database.ContentObserver";
import Uri from "android.net.Uri";
import BitmapFactory from "android.graphics.BitmapFactory";
import Locale from "java.util.Locale";
import WindowManager from "android.view.WindowManager";
import FileObserver from "android.os.FileObserver";
import File from "java.io.File";
import RequiresApi from "androidx.annotation.RequiresApi";
import Environment from "android.os.Environment";
/**
* 读取媒体数据库时需要读取的列,其中 width、height 字段在 API 16 之后才有
*/
const MEDIA_PROJECTIONS_API_16 = arrayOf(
"_data",
MediaStore.Images.ImageColumns.DATE_TAKEN,
MediaStore.Images.ImageColumns.WIDTH,
MediaStore.Images.ImageColumns.HEIGHT
)
/**
* 截屏路径判断的关键字
*/
const KEYWORDS = arrayOf(
"screenshot", "screen_shot", "screen-shot", "screen shot",
"screencapture", "screen_capture", "screen-capture", "screen capture",
"screencap", "screen_cap", "screen-cap", "screen cap"
)
/**
* android 10版本以上通过文件监听实现
*/
@RequiresApi(Build.VERSION_CODES.Q)
class ScreenFileObserver extends FileObserver {
allScreen: File;
constructor(screenFile: File) {
constructor(screenFile: string) {
super(screenFile)
this.allScreen = screenFile;
this.allScreen = File(screenFile);
console.log(allScreen);
}
......@@ -80,21 +49,7 @@ class ScreenFileObserver extends FileObserver {
}
/**
* 屏幕尺寸
*/
let mScreenRealSize: Point | null = getRealScreenSize();
let mHasCallbackPaths: ArrayList<string> = new ArrayList()
let mStartListenTime: number = 0;
let mUiHandler: Handler = Handler(Looper.getMainLooper())
/**
* 内部媒体文件监听器
*/
let mInternalObserver: MediaContentObserver | null = null;
/**
* 外部媒体文件监听器
*/
let mExternalObserver: MediaContentObserver | null = null;
/**
* android 10 版本使用的文件监听器
*/
......@@ -108,178 +63,11 @@ let listenOption: onImageCatchOptions = new onImageCatchOptions();
let lastFileObserverTime: number = 0;
/**
* 处理媒体数据库的内容改变
*/
function handleMediaContentChange(contentUri: Uri) {
let cursor: Cursor | null = null;
try {
cursor = getUniActivity()!.contentResolver.query(
contentUri,
MEDIA_PROJECTIONS_API_16,
null, null,
MediaStore.Images.ImageColumns.DATE_ADDED + " desc limit 1"
)
if (cursor == null) {
return
}
if (!cursor.moveToFirst()) {
return
}
// 获取各列的索引
let dataIndex = cursor.getColumnIndex("_data")
let dateTakenIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.DATE_TAKEN)
let widthIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.WIDTH)
let heightIndex = cursor.getColumnIndex(MediaStore.Images.ImageColumns.HEIGHT)
// 获取行数据
let data = cursor.getString(dataIndex)
let dateTaken = cursor.getLong(dateTakenIndex)
let width:number;
let height:number;
if (widthIndex >= 0 && heightIndex >= 0) {
width = cursor.getInt(widthIndex)
height = cursor.getInt(heightIndex)
} else {
let size = getImageSize(data)
width = size.x
height = size.y
}
// 处理获取到的第一行数据
handleMediaRowData(data, dateTaken, width, height)
} catch (e) {
e.printStackTrace()
} finally {
if (cursor != null && !cursor.isClosed) {
cursor.close()
}
}
}
/**
* 获取媒体库内置图像的大小
*/
function getImageSize(imagePath: string): Point{
let options = BitmapFactory.Options()
options.inJustDecodeBounds = true
BitmapFactory.decodeFile(imagePath, options)
return Point(options.outWidth, options.outHeight)
}
/**
* 处理获取到的一行数据
*/
function handleMediaRowData(data: String, dateTaken: Long, width: Int, height: Int) {
if (checkScreenShot(data, dateTaken, width, height)) {
if (!checkCallback(data)) {
listenOption.onImageCatchChange(data)
}
} else {
// 如果在观察区间媒体数据库有数据改变,又不符合截屏规则
}
}
/**
* 判断指定的数据行是否符合截屏条件
*/
function checkScreenShot(data?: string, dateTaken: Long, width: Int, height: Int): boolean {
// 判断依据一: 时间判断
// 如果加入数据库的时间在开始监听之前, 或者与当前时间相差大于10秒, 则认为当前没有截屏
if (dateTaken < mStartListenTime || System.currentTimeMillis() - dateTaken > 10 * 1000) {
return false
}
// 判断依据二: 尺寸判断
if (mScreenRealSize != null) {
// 如果图片尺寸超出屏幕, 则认为当前没有截屏
if (!(width <= mScreenRealSize!.x && height <= mScreenRealSize!.y)
|| (height <= mScreenRealSize!.x && width <= mScreenRealSize!.y)
) {
return false
}
}
// 判断依据三: 路径判断
if (data == null) {
return false
}
let lowerData = data.lowercase(Locale.getDefault())
// 判断图片路径是否含有指定的关键字之一, 如果有, 则认为当前截屏了
for (keyWork in KEYWORDS) {
if (lowerData.contains(keyWork)) {
return true
}
}
return false
}
/**
* 判断是否已回调过, 某些手机ROM截屏一次会发出多次内容改变的通知; <br></br>
* 删除一个图片也会发通知, 同时防止删除图片时误将上一张符合截屏规则的图片当做是当前截屏.
*/
function checkCallback(imagePath: String): boolean {
if (mHasCallbackPaths.contains(imagePath)) {
return true
}
// 大概缓存15~20条记录便可
if (mHasCallbackPaths.size >= 20) {
// for (i of 4) {
// mHasCallbackPaths.removeAt(0)
// }
}
mHasCallbackPaths.add(imagePath)
return false
}
/**
* 获取屏幕分辨率
*/
function getRealScreenSize(): Point | null {
let screenSize: Point = Point();
try {
let windowManager = getUniActivity()!.getSystemService(Context.WINDOW_SERVICE) as WindowManager
screenSize.x = windowManager.currentWindowMetrics.bounds.width();
screenSize.y = windowManager.currentWindowMetrics.bounds.height();
// let defaultDisplay = windowManager.defaultDisplay
// let defaultDisplay = getUniActivity()!.display
// defaultDisplay!.getRealSize(screenSize)
console.log(screenSize);
} catch (e) {
e.printStackTrace()
}
return screenSize
}
/**
* 媒体内容观察者
*/
class MediaContentObserver extends ContentObserver {
contentUri: Uri;
handler: Handler;
constructor(contentUri: Uri, handler: Handler) {
super(handler)
this.contentUri = contentUri
this.handler = handler
}
override onChange(selfChange: Boolean) {
super.onChange(selfChange)
handleMediaContentChange(contentUri)
}
}
......@@ -306,57 +94,29 @@ export function requestPremission() {
export function onUserCaptureScreen(success: (res: string) => void) {
listenOption.onImageCatchChange = success;
console.log(Build.VERSION.SDK_INT);
console.log(Build.VERSION_CODES.Q);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// android 10 以上版本,使用监听文件的方式,更加可靠
let directory_screenshot: File;
// android 10 以上版本,使用监听文件的方式,更加可靠
let directory_screenshot: File;
// let directory_pictures = getUniActivity()!.getExternalFilesDir(Environment.DIRECTORY_PICTURES)
// let directory_dcim = getUniActivity()!.getExternalFilesDir(Environment.DIRECTORY_DCIM)
let directory_pictures = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
let directory_dcim = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM);
console.log(directory_pictures);
console.log(directory_dcim);
let directory_pictures = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_PICTURES);
let directory_dcim = File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DCIM);
console.log(directory_pictures);
console.log(directory_dcim);
// console.log(directory_pictures2);
// console.log(directory_dcim2);
if (Build.MANUFACTURER.equals("Xiaomi", true)) {
directory_screenshot = File(directory_dcim, "Screenshots");
} else {
directory_screenshot = File(directory_pictures, "Screenshots");
}
if (screenOB != null) {
screenOB!.stopWatching()
}
screenOB = new ScreenFileObserver(directory_screenshot)
screenOB!.startWatching()
if (Build.MANUFACTURER.equals("Xiaomi", true)) {
directory_screenshot = File(directory_dcim, "Screenshots");
} else {
// android 10 以下版本,采用监听系统媒体库的方式
mStartListenTime = System.currentTimeMillis()
// 创建内容观察者
mInternalObserver =
new MediaContentObserver(MediaStore.Images.Media.INTERNAL_CONTENT_URI, mUiHandler)
mExternalObserver =
new MediaContentObserver(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, mUiHandler)
// 注册内容观察者
getUniActivity()!.getContentResolver()!.registerContentObserver(
MediaStore.Images.Media.INTERNAL_CONTENT_URI,
false,
mInternalObserver!
)
getUniActivity()!.getContentResolver()!.registerContentObserver(
MediaStore.Images.Media.EXTERNAL_CONTENT_URI,
false,
mExternalObserver!
)
directory_screenshot = File(directory_pictures, "Screenshots");
}
if (screenOB != null) {
screenOB!.stopWatching()
}
screenOB = new ScreenFileObserver(directory_screenshot.path)
screenOB!.startWatching()
}
......@@ -365,35 +125,13 @@ export function onUserCaptureScreen(success: (res: string) => void) {
*/
export function offUserCaptureScreen(success: (res: string) => void) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
// android 10以上,关闭监听通过移除文件监听器实现
if (screenOB != null) {
screenOB!.stopWatching()
screenOB = null
}
lastFileObserverTime = 0;
} else {
// android 10以下,注销内容观察者
if (mInternalObserver != null) {
try {
getUniActivity()!.contentResolver.unregisterContentObserver(mInternalObserver!)
} catch (e) {
e.printStackTrace()
}
mInternalObserver = null
}
if (mExternalObserver != null) {
try {
getUniActivity()!.contentResolver.unregisterContentObserver(mExternalObserver!)
} catch (e) {
e.printStackTrace()
}
mExternalObserver = null
}
// 清空数据
mStartListenTime = 0
// android 10以上,关闭监听通过移除文件监听器实现
if (screenOB != null) {
screenOB!.stopWatching()
screenOB = null
}
lastFileObserverTime = 0;
success("");
}
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册