自动接收图片并上传到服务器,适用微信、企业微信等所有应用
前言
WorkTool企微机器人可以接收客户群的消息,但接收图片一直是个问题,本文以企业微信为例,实际原理具有广泛适用性,并可扩展到微信等所有APP端。
想要接收企微消息里的图片,你在往下看之前最好确保:
- 你已经使用WorkTool企微机器人并且自己接入过第三方QA(或有回调接口开发能力)
- 有图片OCR或图像识别能力或相关接口
正文
下面开始直接进入接入步骤,首先WorkTool企微机器人是基于无障碍服务为基础的自动化框架而制作的,其读屏技术只能获取到文本内容,对图片等流媒体内容是无法获取到的,这时可能会有两种思路:方案一是图片总是要下载到本地,我们直接监控本地新文件生成就实现了图片获取;方案二是自动截图,我们在发现图片时点开查看然后进行截图,自己保存图片文件。方案一在上文已经有做过介绍(点击查看),本文讲述方案二的实现,工作流程图如下:
WorkTool自研自动化框架,结合 Android 原生提供的录屏/截图工具类 MediaProjection 进行实现。截图核心类如下:
package org.yameida.worktool.activity
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.graphics.Color
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
import android.media.Image
import android.media.ImageReader
import android.media.projection.MediaProjectionManager
import android.os.Bundle
import android.os.Handler
import android.os.Looper
import android.os.SystemClock
import androidx.appcompat.app.AppCompatActivity
import com.blankj.utilcode.constant.PermissionConstants
import com.blankj.utilcode.util.*
import org.yameida.worktool.utils.capture.AndroidUtils
import org.yameida.worktool.utils.capture.MediaProjectionHolder
import android.util.DisplayMetrics
import android.view.WindowManager
import org.yameida.worktool.utils.startServiceSafe
import org.yameida.worktool.service.PlayNotifyService
import org.yameida.worktool.service.fastStartActivity
import java.lang.Exception
/**
* Created by Gallon on 2019/7/30.
*/
class GetScreenShotActivity : AppCompatActivity() {
private var mediaProjectionManager: MediaProjectionManager? = null
private var hideFloatWindow = false
private val handler = Handler(Looper.getMainLooper())
companion object {
val HIDE_FLOAT_WINDOW = "hideFloatWindow"
fun startCapture(): Bitmap? {
if (MediaProjectionHolder.mMediaProjection == null) {
LogUtils.e("截图失败 mediaProjection未初始化")
fastStartActivity(Utils.getApp(), GetScreenShotActivity::class.java)
return null
}
val imageReader = ImageReader.newInstance(ScreenUtils.getScreenWidth(), ScreenUtils.getScreenHeight(), PixelFormat.RGBA_8888, 1)
val virtualDisplay = MediaProjectionHolder.mMediaProjection?.createVirtualDisplay("ScreenShout",
ScreenUtils.getScreenWidth(), ScreenUtils.getScreenHeight(), ScreenUtils.getScreenDensityDpi(),
DisplayManager.VIRTUAL_DISPLAY_FLAG_AUTO_MIRROR,
imageReader.surface, null, null)
var image: Image? = null
var tryCount = 0
while (tryCount < 10 && image == null) {
SystemClock.sleep(250)
image = imageReader.acquireNextImage()
}
if (image == null) {
LogUtils.i("GetScreenShotActivity", "image is null.")
return null
}
val width = image.width
val height = image.height
val planes = image.planes
val buffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride
val rowPadding = rowStride - pixelStride * width
var bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888)
bitmap.copyPixelsFromBuffer(buffer)
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height)
image.close()
imageReader.close()
virtualDisplay?.release()
return bitmap
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
LogUtils.i("onCreate")
window.statusBarColor = Color.TRANSPARENT
mediaProjectionManager = Utils.getApp().getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
hideFloatWindow = intent.getBooleanExtra(HIDE_FLOAT_WINDOW, false)
PermissionUtils.permission(PermissionConstants.STORAGE)
.callback(object : PermissionUtils.SimpleCallback {
override fun onGranted() {
LogUtils.d("start record")
mediaProjectionManager?.apply {
val intent = this.createScreenCaptureIntent()
if (packageManager.resolveActivity(intent, PackageManager.MATCH_DEFAULT_ONLY) != null) {
startActivityForResult(intent, 0)
} else {
ToastUtils.showShort("抱歉,你的手机暂不支持录屏")
}
}
}
override fun onDenied() {
ToastUtils.showShort("请允许申请的权限,否则无法录屏")
finish()
}
})
.request()
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
LogUtils.i("onActivityResult")
if (resultCode == Activity.RESULT_OK && data != null) {
LogUtils.e("mediaProjectionManager: $mediaProjectionManager")
LogUtils.e("resultCode: $resultCode")
LogUtils.e("data: $data")
try {
val mWindowManager = getSystemService(WINDOW_SERVICE) as WindowManager
val metrics = DisplayMetrics()
mWindowManager.defaultDisplay.getMetrics(metrics)
} catch (e: Exception) {
LogUtils.e("MediaProjection error")
}
sendBroadcast(Intent())
val service = Intent(this, PlayNotifyService::class.java)
service.putExtra("setMediaProject", true)
service.putExtra("code", resultCode)
service.putExtra("data", data)
startServiceSafe(service)
}
finish()
}
}
点击图片进入图片浏览页面调用截图 startCapture 方法即可获取到 bitmap,当然 bitmap 的数据大小我们可以缓存一下,如果发现与上一次不一致则我们就可以认为有新的图片文件被获取了,然后推送到我们的目标服务器上,后台再进行对图片的处理和分析然后选择性的进行回复(调用 WorkTool 发送消息指令)即可。
WorkTool 企微机器人的API文档地址: https://worktool.apifox.cn/
总结
至此,你应该明白了自动化框架实现图片获取的原理,但这种方案还是存在不稳定的情况,比如截图可能会因为屏幕出现其他信息而导致两张图片的一致性判断失效,而且本方案的速度确实要更慢一点,我们在调研有没有更好的办法或对其速度和稳定性进行优化。WorkTool 企微机器人可以自动将接收到的图片推送给第三方系统,如果还没了解过 WorkTool 可以先看下官网 或入门教程尝试一下。