update 图像识别截图和前台服务
This commit is contained in:
@@ -70,6 +70,12 @@
|
|||||||
android:windowSoftInputMode="adjustUnspecified|stateHidden"
|
android:windowSoftInputMode="adjustUnspecified|stateHidden"
|
||||||
android:theme="@style/AppTheme">
|
android:theme="@style/AppTheme">
|
||||||
</activity>
|
</activity>
|
||||||
|
<activity
|
||||||
|
android:name=".activity.GetScreenShotActivity"
|
||||||
|
android:autoRemoveFromRecents="true"
|
||||||
|
android:excludeFromRecents="true"
|
||||||
|
android:launchMode="singleInstance"
|
||||||
|
android:theme="@style/AppTheme.TranslucentNoTitleFullscreen" />
|
||||||
<service
|
<service
|
||||||
android:name="org.yameida.worktool.service.WeworkService"
|
android:name="org.yameida.worktool.service.WeworkService"
|
||||||
android:enabled="true"
|
android:enabled="true"
|
||||||
@@ -84,6 +90,9 @@
|
|||||||
android:name="android.accessibilityservice"
|
android:name="android.accessibilityservice"
|
||||||
android:resource="@xml/accessibility_service_config" />
|
android:resource="@xml/accessibility_service_config" />
|
||||||
</service>
|
</service>
|
||||||
|
<service
|
||||||
|
android:foregroundServiceType="mediaProjection"
|
||||||
|
android:name=".service.PlayNotifyService" />
|
||||||
|
|
||||||
<provider
|
<provider
|
||||||
android:name=".utils.GenericFileProvider"
|
android:name=".utils.GenericFileProvider"
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ object Constant {
|
|||||||
var pushImage = false
|
var pushImage = false
|
||||||
var autoPublishComment = false
|
var autoPublishComment = false
|
||||||
var groupQrCode = false
|
var groupQrCode = false
|
||||||
|
var enableMediaProject = false
|
||||||
var robotId: String
|
var robotId: String
|
||||||
get() = SPUtils.getInstance().getString("robotId", SPUtils.getInstance().getString("LISTEN_CHANNEL_ID", ""))
|
get() = SPUtils.getInstance().getString("robotId", SPUtils.getInstance().getString("LISTEN_CHANNEL_ID", ""))
|
||||||
set(value) {
|
set(value) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import com.hjq.toast.ToastUtils
|
|||||||
import com.tendcloud.tenddata.TalkingDataSDK
|
import com.tendcloud.tenddata.TalkingDataSDK
|
||||||
import com.umeng.commonsdk.UMConfigure
|
import com.umeng.commonsdk.UMConfigure
|
||||||
import org.yameida.worktool.config.GlobalException
|
import org.yameida.worktool.config.GlobalException
|
||||||
|
import org.yameida.worktool.notification.PlayNotifyManager
|
||||||
import org.yameida.worktool.utils.IWWAPIUtil
|
import org.yameida.worktool.utils.IWWAPIUtil
|
||||||
import update.UpdateAppUtils
|
import update.UpdateAppUtils
|
||||||
|
|
||||||
@@ -52,6 +53,8 @@ class MyApplication : Application() {
|
|||||||
IWWAPIUtil.init(this)
|
IWWAPIUtil.init(this)
|
||||||
//初始化自动更新
|
//初始化自动更新
|
||||||
UpdateAppUtils.init(this)
|
UpdateAppUtils.init(this)
|
||||||
|
//初始化前台服务
|
||||||
|
PlayNotifyManager.show()
|
||||||
//设置全局异常捕获重启
|
//设置全局异常捕获重启
|
||||||
Thread.setDefaultUncaughtExceptionHandler(GlobalException.getInstance())
|
Thread.setDefaultUncaughtExceptionHandler(GlobalException.getInstance())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,159 @@
|
|||||||
|
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 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() {
|
||||||
|
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)
|
||||||
|
|
||||||
|
val imageName = System.currentTimeMillis().toString() + ".png"
|
||||||
|
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
|
||||||
|
}
|
||||||
|
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()
|
||||||
|
|
||||||
|
if (bitmap != null) {
|
||||||
|
AndroidUtils.bitmapToFile(bitmap, imageName)
|
||||||
|
}
|
||||||
|
ToastUtils.showShort("截图已保存到sdcard/recorder/screenShot~~~")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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("hahaha1")
|
||||||
|
LogUtils.e("mediaProjectionManager: $mediaProjectionManager")
|
||||||
|
LogUtils.e("resultCode: $resultCode")
|
||||||
|
LogUtils.e("data: $data")
|
||||||
|
// MediaProjectionHolder.setMediaProjection(mediaProjectionManager!!.getMediaProjection(resultCode, data))
|
||||||
|
// LogUtils.e("hahaha2")
|
||||||
|
// if (hideFloatWindow) {
|
||||||
|
// if (FloatWindowManager.isShow) {
|
||||||
|
// FloatWindowManager.hide()
|
||||||
|
// handler.postDelayed({
|
||||||
|
// SystemClock.sleep(100)
|
||||||
|
// startCapture()
|
||||||
|
// FloatWindowManager.show()
|
||||||
|
// }, 200)
|
||||||
|
// } else {
|
||||||
|
// SystemClock.sleep(200)
|
||||||
|
// startCapture()
|
||||||
|
// }
|
||||||
|
// } else {
|
||||||
|
// startCapture()
|
||||||
|
// }
|
||||||
|
// startCapture()
|
||||||
|
|
||||||
|
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)
|
||||||
|
|
||||||
|
LogUtils.e("hahaha3")
|
||||||
|
}
|
||||||
|
finish()
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -10,10 +10,14 @@ import com.umeng.analytics.MobclickAgent
|
|||||||
import kotlinx.android.synthetic.main.activity_listen.*
|
import kotlinx.android.synthetic.main.activity_listen.*
|
||||||
import org.yameida.worktool.*
|
import org.yameida.worktool.*
|
||||||
import android.content.*
|
import android.content.*
|
||||||
|
import android.os.IBinder
|
||||||
import android.text.InputType
|
import android.text.InputType
|
||||||
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
import com.google.android.material.dialog.MaterialAlertDialogBuilder
|
||||||
import com.qmuiteam.qmui.widget.dialog.QMUIDialog
|
import com.qmuiteam.qmui.widget.dialog.QMUIDialog
|
||||||
|
import org.yameida.worktool.service.PlayNotifyService
|
||||||
|
import org.yameida.worktool.service.fastStartActivity
|
||||||
import org.yameida.worktool.utils.*
|
import org.yameida.worktool.utils.*
|
||||||
|
import org.yameida.worktool.utils.capture.MediaProjectionHolder
|
||||||
import org.yameida.worktool.utils.envcheck.CheckHook
|
import org.yameida.worktool.utils.envcheck.CheckHook
|
||||||
import org.yameida.worktool.utils.envcheck.CheckRoot
|
import org.yameida.worktool.utils.envcheck.CheckRoot
|
||||||
|
|
||||||
@@ -43,6 +47,7 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
initAccessibility()
|
initAccessibility()
|
||||||
initOverlays()
|
initOverlays()
|
||||||
initData()
|
initData()
|
||||||
|
initNotification()
|
||||||
PermissionUtils.permission("android.permission.READ_EXTERNAL_STORAGE").request()
|
PermissionUtils.permission("android.permission.READ_EXTERNAL_STORAGE").request()
|
||||||
registerReceiver(openWsReceiver, IntentFilter(Constant.WEWORK_NOTIFY))
|
registerReceiver(openWsReceiver, IntentFilter(Constant.WEWORK_NOTIFY))
|
||||||
}
|
}
|
||||||
@@ -160,6 +165,24 @@ class ListenActivity : AppCompatActivity() {
|
|||||||
HttpUtil.getMyConfig(toast = false)
|
HttpUtil.getMyConfig(toast = false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun initNotification() {
|
||||||
|
if (!Constant.enableMediaProject) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
application.bindService(Intent(application, PlayNotifyService::class.java), object : ServiceConnection {
|
||||||
|
override fun onServiceDisconnected(name: ComponentName) {
|
||||||
|
LogUtils.d("onServiceDisconnected")
|
||||||
|
}
|
||||||
|
override fun onServiceConnected(name: ComponentName, iBinder: IBinder) {
|
||||||
|
LogUtils.d("onServiceConnected")
|
||||||
|
}
|
||||||
|
}, Context.BIND_AUTO_CREATE)
|
||||||
|
//开启屏幕录制权限
|
||||||
|
if (MediaProjectionHolder.mMediaProjection == null) {
|
||||||
|
fastStartActivity(this, GetScreenShotActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun showSelectHostDialog() {
|
private fun showSelectHostDialog() {
|
||||||
val hostList = SPUtils.getInstance().getStringSet("host_list", mutableSetOf(Constant.host))
|
val hostList = SPUtils.getInstance().getStringSet("host_list", mutableSetOf(Constant.host))
|
||||||
if (hostList.isNotEmpty()) {
|
if (hostList.isNotEmpty()) {
|
||||||
|
|||||||
@@ -0,0 +1,36 @@
|
|||||||
|
package org.yameida.worktool.notification
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import android.content.Intent
|
||||||
|
import com.blankj.utilcode.util.Utils
|
||||||
|
import org.yameida.worktool.utils.startServiceSafe
|
||||||
|
import org.yameida.worktool.service.PlayNotifyService
|
||||||
|
import java.lang.reflect.Method
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gallon on 2019/8/7.
|
||||||
|
*/
|
||||||
|
object PlayNotifyManager {
|
||||||
|
private val TAG = PlayNotifyManager::class.java.simpleName
|
||||||
|
|
||||||
|
private var context = Utils.getApp()
|
||||||
|
@SuppressLint("WrongConstant")
|
||||||
|
private val statusBarManager = context.getSystemService("statusbar")
|
||||||
|
|
||||||
|
var isShow = false
|
||||||
|
|
||||||
|
fun show() {
|
||||||
|
context.startServiceSafe(Intent(context, PlayNotifyService::class.java))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var collapse: Method? = null
|
||||||
|
fun collapseStatusBar() {
|
||||||
|
try {
|
||||||
|
if (collapse == null) collapse = statusBarManager::class.java.getMethod("collapsePanels").apply { isAccessible = true }
|
||||||
|
collapse?.invoke(statusBarManager)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
package org.yameida.worktool.service
|
package org.yameida.worktool.service
|
||||||
|
|
||||||
|
import android.app.PendingIntent
|
||||||
|
import android.content.Context
|
||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.graphics.Rect
|
import android.graphics.Rect
|
||||||
import android.view.accessibility.AccessibilityNodeInfo
|
import android.view.accessibility.AccessibilityNodeInfo
|
||||||
@@ -15,6 +17,19 @@ import org.yameida.worktool.utils.FloatWindowHelper
|
|||||||
import org.yameida.worktool.utils.Views
|
import org.yameida.worktool.utils.Views
|
||||||
import java.lang.Exception
|
import java.lang.Exception
|
||||||
|
|
||||||
|
var requestCode = 1000000
|
||||||
|
fun fastStartActivity(context: Context, clazz: Class<*>, flags: Int = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP, i: Intent? = null) {
|
||||||
|
val intent = i ?: Intent(context, clazz)
|
||||||
|
intent.flags = flags
|
||||||
|
val pendingIntent = PendingIntent.getActivity(context, requestCode++, intent, 0)
|
||||||
|
try {
|
||||||
|
pendingIntent.send()
|
||||||
|
} catch (e: PendingIntent.CanceledException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
context.startActivity(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 进入首页-消息页
|
* 进入首页-消息页
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -0,0 +1,107 @@
|
|||||||
|
package org.yameida.worktool.service
|
||||||
|
|
||||||
|
import android.app.*
|
||||||
|
import android.content.BroadcastReceiver
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.content.IntentFilter
|
||||||
|
import android.graphics.Color
|
||||||
|
import android.media.projection.MediaProjectionManager
|
||||||
|
import android.os.Binder
|
||||||
|
import android.os.Build
|
||||||
|
import android.os.IBinder
|
||||||
|
import android.os.SystemClock
|
||||||
|
import android.widget.RemoteViews
|
||||||
|
import androidx.core.app.NotificationCompat
|
||||||
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
import com.blankj.utilcode.util.Utils
|
||||||
|
import org.yameida.worktool.R
|
||||||
|
import org.yameida.worktool.activity.GetScreenShotActivity
|
||||||
|
import org.yameida.worktool.utils.capture.MediaProjectionHolder
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gallon on 2019/8/7.
|
||||||
|
*/
|
||||||
|
class PlayNotifyService : Service() {
|
||||||
|
private val TAG = PlayNotifyService::class.java.simpleName
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private const val PLAY_NOTIFY_ID = 0x1216
|
||||||
|
private const val CHANNEL_ONE_ID = "RECORD_CHANNEL_ONE_ID"
|
||||||
|
private const val CHANNEL_ONE_NAME = "RECORD_CHANNEL_ONE_NAME"
|
||||||
|
const val PLAY_NOTIFY = "record_notify"
|
||||||
|
const val PLAY_NOTIFY_CODE = "record_notify_code"
|
||||||
|
private var playNotifyReceiver: PlayNotifyReceiver = PlayNotifyReceiver()
|
||||||
|
}
|
||||||
|
private var context = Utils.getApp()
|
||||||
|
private var manager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
|
||||||
|
private var notification: Notification? = null
|
||||||
|
|
||||||
|
inner class PlayNotifyServiceBinder : Binder() {
|
||||||
|
fun getService() = this@PlayNotifyService
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onBind(intent: Intent?): IBinder? = PlayNotifyServiceBinder()
|
||||||
|
|
||||||
|
override fun onDestroy() {
|
||||||
|
super.onDestroy()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
|
||||||
|
LogUtils.d(TAG, "onStartCommand: $intent")
|
||||||
|
show(null)
|
||||||
|
initBroadcastReceivers()
|
||||||
|
if (intent?.getBooleanExtra("setMediaProject", false) == true) {
|
||||||
|
val resultCode = intent.getIntExtra("code", -1)
|
||||||
|
val data = intent.getParcelableExtra<Intent>("data")
|
||||||
|
val mediaProjectionManager = Utils.getApp().getSystemService(Context.MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
|
||||||
|
MediaProjectionHolder.setMediaProjection(mediaProjectionManager.getMediaProjection(resultCode, data!!))
|
||||||
|
}
|
||||||
|
return super.onStartCommand(intent, flags, startId)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun show(view: RemoteViews?) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
val channel = NotificationChannel(CHANNEL_ONE_ID, CHANNEL_ONE_NAME, NotificationManager.IMPORTANCE_DEFAULT)
|
||||||
|
channel.enableLights(true) //是否在桌面icon右上角展示小红点
|
||||||
|
channel.lightColor = Color.GREEN //小红点颜色
|
||||||
|
channel.setShowBadge(true) //是否在久按桌面图标时显示此渠道的通知
|
||||||
|
channel.setSound(null, null) //静音 NOTE:首次安装生效 否则需要APP清除数据生效
|
||||||
|
manager.createNotificationChannel(channel)
|
||||||
|
}
|
||||||
|
val notificationBuilder = NotificationCompat.Builder(context, CHANNEL_ONE_ID)
|
||||||
|
.setCustomContentView(view)
|
||||||
|
.setWhen(System.currentTimeMillis())
|
||||||
|
.setAutoCancel(false)
|
||||||
|
.setOngoing(true)
|
||||||
|
.setSmallIcon(R.mipmap.ic_launcher)
|
||||||
|
.setOngoing(true)
|
||||||
|
notification = notificationBuilder.build()
|
||||||
|
startForeground(PLAY_NOTIFY_ID, notification)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initBroadcastReceivers() {
|
||||||
|
val filter = IntentFilter(PLAY_NOTIFY)
|
||||||
|
context.registerReceiver(playNotifyReceiver, filter)
|
||||||
|
}
|
||||||
|
|
||||||
|
private class PlayNotifyReceiver : BroadcastReceiver() {
|
||||||
|
|
||||||
|
override fun onReceive(context: Context, intent: Intent) {
|
||||||
|
val code = intent.getIntExtra(PLAY_NOTIFY_CODE, -1)
|
||||||
|
if (code == -1) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
LogUtils.d("notification click: $code")
|
||||||
|
if (MediaProjectionHolder.mMediaProjection != null) {
|
||||||
|
SystemClock.sleep(300)
|
||||||
|
GetScreenShotActivity.startCapture()
|
||||||
|
} else {
|
||||||
|
fastStartActivity(context, GetScreenShotActivity::class.java)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -256,7 +256,7 @@ object WeworkGetImpl {
|
|||||||
for (i in 0 until list.childCount) {
|
for (i in 0 until list.childCount) {
|
||||||
val item = list.getChild(i)
|
val item = list.getChild(i)
|
||||||
val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView)
|
val tvList = AccessibilityUtil.findAllOnceByClazz(item, Views.TextView)
|
||||||
val textList = tvList.filter { it.text != null }.map { it.text.toString() }
|
val textList = tvList.mapNotNull { it.text?.toString() }
|
||||||
if (textList.isNotEmpty()) {
|
if (textList.isNotEmpty()) {
|
||||||
corpList.add(textList[0])
|
corpList.add(textList[0])
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,53 @@
|
|||||||
|
package org.yameida.worktool.utils
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Build
|
||||||
|
import android.view.View
|
||||||
|
import com.blankj.utilcode.util.EncryptUtils
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
fun getFileMD5(file: java.io.File): String = file.getMD5()
|
||||||
|
|
||||||
|
fun java.io.File.getMD5(): String {
|
||||||
|
val digest = EncryptUtils.encryptMD5File(this)
|
||||||
|
val strHex = arrayOf("0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "a", "b", "c", "d", "e", "f")
|
||||||
|
val sb = StringBuilder()
|
||||||
|
for (byte in digest) {
|
||||||
|
var d = byte.toInt()
|
||||||
|
if (d < 0) d += 256
|
||||||
|
val d1 = d / 16
|
||||||
|
val d2 = d % 16
|
||||||
|
sb.append(strHex[d1] + strHex[d2])
|
||||||
|
}
|
||||||
|
return sb.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Long.toTimeString(): String {
|
||||||
|
val i = this.toInt() / 1000
|
||||||
|
return String.format("%02d:%02d", Integer.valueOf(i / 60), Integer.valueOf(i % 60))
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Context.startServiceSafe(intent: Intent) {
|
||||||
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
|
||||||
|
startForegroundService(intent)
|
||||||
|
} else {
|
||||||
|
startService(intent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.hideBottomNav() {
|
||||||
|
val decorView = this.window.decorView
|
||||||
|
decorView.systemUiVisibility = 0
|
||||||
|
val uiOptions = (View.SYSTEM_UI_FLAG_HIDE_NAVIGATION
|
||||||
|
or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY or View.SYSTEM_UI_FLAG_FULLSCREEN or
|
||||||
|
View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN)
|
||||||
|
decorView.setSystemUiVisibility(uiOptions)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun Activity.showBottomNav() {
|
||||||
|
val decorView = this.window.decorView
|
||||||
|
decorView.systemUiVisibility = 0
|
||||||
|
}
|
||||||
@@ -0,0 +1,128 @@
|
|||||||
|
package org.yameida.worktool.utils.capture
|
||||||
|
|
||||||
|
import android.app.ActivityManager
|
||||||
|
import android.content.Context
|
||||||
|
import android.content.pm.PackageManager
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.graphics.drawable.Drawable
|
||||||
|
import android.graphics.drawable.StateListDrawable
|
||||||
|
import android.media.MediaMetadataRetriever
|
||||||
|
import android.os.Process
|
||||||
|
import android.text.TextUtils
|
||||||
|
import android.view.View
|
||||||
|
import android.view.inputmethod.InputMethodManager
|
||||||
|
import com.blankj.utilcode.util.LogUtils
|
||||||
|
import com.blankj.utilcode.util.Utils
|
||||||
|
import java.io.BufferedOutputStream
|
||||||
|
import java.io.File
|
||||||
|
import java.io.FileOutputStream
|
||||||
|
import java.text.SimpleDateFormat
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by gallon on 2019/7/9.
|
||||||
|
*/
|
||||||
|
class AndroidUtils {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
private val TAG = AndroidUtils::class.java.simpleName
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun hideKeyboard(view: View?) {
|
||||||
|
if (view != null) {
|
||||||
|
val imm = view.context.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager
|
||||||
|
if (imm.isActive) {
|
||||||
|
imm.hideSoftInputFromWindow(view.windowToken, 0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getMediaDurationTime(filePath: String): Long {
|
||||||
|
var durationTime: Long = 0
|
||||||
|
val retriever = MediaMetadataRetriever()
|
||||||
|
try {
|
||||||
|
retriever.setDataSource(filePath)
|
||||||
|
val v1 = retriever.extractMetadata(9)
|
||||||
|
if (TextUtils.isEmpty(v1)) {
|
||||||
|
LogUtils.d(TAG, "Extract metadata failed.")
|
||||||
|
}
|
||||||
|
durationTime = java.lang.Long.parseLong(v1)
|
||||||
|
} catch (e: Exception) {
|
||||||
|
e.printStackTrace()
|
||||||
|
LogUtils.d(TAG, "exception = " + e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
retriever.release()
|
||||||
|
} catch (e: Exception) {
|
||||||
|
LogUtils.d(TAG, "retriever.release() Exception = " + e.message)
|
||||||
|
}
|
||||||
|
|
||||||
|
return durationTime
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun hasPermission(context: Context, permission: String): Boolean {
|
||||||
|
return try {
|
||||||
|
context.checkPermission(permission, Process.myPid(), Process.myUid()) == PackageManager.PERMISSION_GRANTED
|
||||||
|
} catch (e: SecurityException) {
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getTimeFileName(time: Long, type: String): String {
|
||||||
|
return try {
|
||||||
|
SimpleDateFormat(type).format(Date(time))
|
||||||
|
} catch (e: Exception) {
|
||||||
|
""
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun getRunningAppProcesses(context: Context, packageName: String): Boolean {
|
||||||
|
val appProcesses = (context.getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager).runningAppProcesses ?: return false
|
||||||
|
for (appProcess in appProcesses) {
|
||||||
|
if (appProcess.importance == 100 && appProcess.processName == packageName) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun setPressedBg(view: View, normal: Drawable?, focused: Drawable?, pressed: Drawable?) {
|
||||||
|
val bg = StateListDrawable()
|
||||||
|
val states = arrayOfNulls<IntArray>(6)
|
||||||
|
states[0] = intArrayOf(16842919, 16842910)
|
||||||
|
states[1] = intArrayOf(16842910, 16842908)
|
||||||
|
states[2] = intArrayOf(16842910)
|
||||||
|
states[3] = intArrayOf(16842908, 16842909)
|
||||||
|
states[4] = intArrayOf(16842909)
|
||||||
|
bg.addState(states[0], pressed)
|
||||||
|
bg.addState(states[3], focused)
|
||||||
|
bg.addState(states[2], normal)
|
||||||
|
view.background = bg
|
||||||
|
}
|
||||||
|
|
||||||
|
fun bitmapToFile(bitmap: Bitmap, fileName: String, path: String = "screenShot"): String {
|
||||||
|
val dir = File(Utils.getApp().getExternalFilesDir("capture"), path)
|
||||||
|
if (!dir.exists()) {
|
||||||
|
dir.mkdirs()
|
||||||
|
}
|
||||||
|
val myCaptureFile = File(dir, fileName)
|
||||||
|
if (!myCaptureFile.exists()) {
|
||||||
|
myCaptureFile.createNewFile()
|
||||||
|
}
|
||||||
|
val bos = BufferedOutputStream(FileOutputStream(myCaptureFile))
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.JPEG, 75, bos)
|
||||||
|
bos.flush()
|
||||||
|
bos.close()
|
||||||
|
return myCaptureFile.absolutePath
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,92 @@
|
|||||||
|
package org.yameida.worktool.utils.capture
|
||||||
|
|
||||||
|
import android.graphics.BitmapFactory
|
||||||
|
import android.graphics.Bitmap
|
||||||
|
import android.provider.MediaStore
|
||||||
|
import android.app.Activity
|
||||||
|
import android.graphics.Matrix
|
||||||
|
import android.media.ExifInterface
|
||||||
|
import android.net.Uri
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gallon on 2019/9/1.
|
||||||
|
*/
|
||||||
|
object BitmapUtil {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取图片的旋转角度
|
||||||
|
*
|
||||||
|
* @param path 图片绝对路径
|
||||||
|
* @return 图片的旋转角度
|
||||||
|
*/
|
||||||
|
fun getBitmapDegree(path: String): Int {
|
||||||
|
var degree = 0
|
||||||
|
try {
|
||||||
|
// 从指定路径下读取图片,并获取其EXIF信息
|
||||||
|
val exifInterface = ExifInterface(path)
|
||||||
|
// 获取图片的旋转信息
|
||||||
|
val orientation = exifInterface.getAttributeInt(ExifInterface.TAG_ORIENTATION, ExifInterface.ORIENTATION_NORMAL)
|
||||||
|
when (orientation) {
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_90 -> degree = 90
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_180 -> degree = 180
|
||||||
|
ExifInterface.ORIENTATION_ROTATE_270 -> degree = 270
|
||||||
|
}
|
||||||
|
} catch (e: IOException) {
|
||||||
|
e.printStackTrace()
|
||||||
|
}
|
||||||
|
|
||||||
|
return degree
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将图片按照指定的角度进行旋转
|
||||||
|
*
|
||||||
|
* @param bitmap 需要旋转的图片
|
||||||
|
* @param degree 指定的旋转角度
|
||||||
|
* @return 旋转后的图片
|
||||||
|
*/
|
||||||
|
fun rotateBitmapByDegree(bitmap: Bitmap, degree: Float): Bitmap {
|
||||||
|
// 根据旋转角度,生成旋转矩阵
|
||||||
|
val matrix = Matrix()
|
||||||
|
matrix.postRotate(degree)
|
||||||
|
// 将原始图片按照旋转矩阵进行旋转,并得到新的图片
|
||||||
|
val newBitmap = Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true)
|
||||||
|
if (!bitmap.isRecycled) {
|
||||||
|
bitmap.recycle()
|
||||||
|
}
|
||||||
|
return newBitmap
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取我们需要的整理过旋转角度的Uri
|
||||||
|
* @param activity 上下文环境
|
||||||
|
* @param path 路径
|
||||||
|
* @return 正常的Uri
|
||||||
|
*/
|
||||||
|
fun getRotatedUri(activity: Activity, path: String): Uri {
|
||||||
|
val degree = getBitmapDegree(path)
|
||||||
|
if (degree != 0) {
|
||||||
|
val bitmap = BitmapFactory.decodeFile(path)
|
||||||
|
val newBitmap = rotateBitmapByDegree(bitmap, degree.toFloat())
|
||||||
|
return Uri.parse(MediaStore.Images.Media.insertImage(activity.contentResolver, newBitmap, null, null))
|
||||||
|
} else {
|
||||||
|
return Uri.fromFile(File(path))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将图片按照指定的角度进行旋转
|
||||||
|
*
|
||||||
|
* @param path 需要旋转的图片的路径
|
||||||
|
* @param degree 指定的旋转角度
|
||||||
|
* @return 旋转后的图片
|
||||||
|
*/
|
||||||
|
fun rotateBitmapByDegree(path: String, degree: Float): Bitmap {
|
||||||
|
val bitmap = BitmapFactory.decodeFile(path)
|
||||||
|
return rotateBitmapByDegree(bitmap, degree)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
103
app/src/main/java/org/yameida/worktool/utils/capture/Color.java
Normal file
103
app/src/main/java/org/yameida/worktool/utils/capture/Color.java
Normal file
@@ -0,0 +1,103 @@
|
|||||||
|
package org.yameida.worktool.utils.capture;
|
||||||
|
|
||||||
|
public class Color {
|
||||||
|
private int R = 0;
|
||||||
|
private int G = 0;
|
||||||
|
private int B = 0;
|
||||||
|
|
||||||
|
|
||||||
|
public Color(int r, int g, int b) {
|
||||||
|
R = r;
|
||||||
|
G = g;
|
||||||
|
B = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color(int b, int g, int r, Boolean bgr) {
|
||||||
|
if (bgr) {
|
||||||
|
R = r;
|
||||||
|
G = g;
|
||||||
|
B = b;
|
||||||
|
} else {
|
||||||
|
R = b;
|
||||||
|
G = g;
|
||||||
|
B = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color(int color) {
|
||||||
|
R = android.graphics.Color.red(color);
|
||||||
|
G = android.graphics.Color.green(color);
|
||||||
|
B = android.graphics.Color.blue(color);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Color() {
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getR() {
|
||||||
|
return R;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setR(int r) {
|
||||||
|
R = r;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getG() {
|
||||||
|
return G;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setG(int g) {
|
||||||
|
G = g;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getB() {
|
||||||
|
return B;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setB(int b) {
|
||||||
|
B = b;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int[] getDexRGB() {
|
||||||
|
return new int[]{R, G, B};
|
||||||
|
}
|
||||||
|
|
||||||
|
public int[] getDexBGR() {
|
||||||
|
return new int[]{B, G, R};
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断2个颜色是否相同,由于图像渲染叠加,同一个icon可能每次渲染的色值不完全相同,所以判断的时候加入了误差
|
||||||
|
*
|
||||||
|
* @param color1
|
||||||
|
* @param color2
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isSame(Color color1, Color color2) {
|
||||||
|
return isSame(color1, color2, 30);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断颜色是否相同,自定义误差
|
||||||
|
*
|
||||||
|
* @param color1
|
||||||
|
* @param color2
|
||||||
|
* @param offset
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static boolean isSame(Color color1, Color color2, int offset) {
|
||||||
|
|
||||||
|
// 算法: R G B这3个通道的色值差的绝对值之和小于offset
|
||||||
|
return (Math.abs(color1.getR() - color2.getR()) + Math.abs(color1.getG() - color2.getG()) + Math.abs(color1.getB() - color2.getB())) <= offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean equals(Color obj) {
|
||||||
|
return obj.getR() == R && obj.getG() == G && obj.getB() == B;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "" + R + "," + G + "," + B;
|
||||||
|
}
|
||||||
|
}
|
||||||
463
app/src/main/java/org/yameida/worktool/utils/capture/Image.java
Normal file
463
app/src/main/java/org/yameida/worktool/utils/capture/Image.java
Normal file
@@ -0,0 +1,463 @@
|
|||||||
|
package org.yameida.worktool.utils.capture;
|
||||||
|
|
||||||
|
import android.app.Dialog;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.BitmapFactory;
|
||||||
|
import android.graphics.Matrix;
|
||||||
|
import android.util.Base64;
|
||||||
|
import android.view.Window;
|
||||||
|
import android.view.WindowManager;
|
||||||
|
import android.widget.ImageView;
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.LogUtils;
|
||||||
|
|
||||||
|
//import org.opencv.android.Utils;
|
||||||
|
//import org.opencv.core.Core;
|
||||||
|
//import org.opencv.core.CvType;
|
||||||
|
//import org.opencv.core.Mat;
|
||||||
|
//import org.opencv.imgproc.Imgproc;
|
||||||
|
|
||||||
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileNotFoundException;
|
||||||
|
import java.io.FileOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
|
||||||
|
|
||||||
|
public class Image {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 保存图片到图库
|
||||||
|
* Image.saveImageToGallery(bt, getExternalFilesDir("").getAbsolutePath() + "/asdf.png");
|
||||||
|
*
|
||||||
|
* @param bmp
|
||||||
|
*/
|
||||||
|
public static void saveImageToGallery(Bitmap bmp, String bitName) {
|
||||||
|
File file = new File(bitName);
|
||||||
|
try {
|
||||||
|
FileOutputStream fos = new FileOutputStream(file);
|
||||||
|
bmp.compress(Bitmap.CompressFormat.PNG, 100, fos);
|
||||||
|
fos.flush();
|
||||||
|
fos.close();
|
||||||
|
} catch (FileNotFoundException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IOException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 打开本地图片
|
||||||
|
*
|
||||||
|
* @param path
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Bitmap openImg(String path) {
|
||||||
|
|
||||||
|
Bitmap ret = BitmapFactory.decodeFile(path);
|
||||||
|
|
||||||
|
if (ret == null) {
|
||||||
|
LogUtils.e("打开" + path + "失败!");
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 单点找色
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param color
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static LinkedList<Point> findPoint(Bitmap img, Color color) {
|
||||||
|
LinkedList<Point> pl = new LinkedList<Point>();
|
||||||
|
int width = img.getWidth();
|
||||||
|
int height = img.getHeight();
|
||||||
|
for (int i = 0; i < width; i++) {
|
||||||
|
for (int j = 0; j < height; j++) {
|
||||||
|
if (Color.isSame(getPoint(img, i, j), color)) {
|
||||||
|
pl.add(new Point(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return pl;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多色找点
|
||||||
|
* 在屏幕某个范围内
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param colorRules
|
||||||
|
* @param leftX
|
||||||
|
* @param leftY
|
||||||
|
* @param rightX
|
||||||
|
* @param rightY
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Point findPointByMulColor(Bitmap img, String colorRules, int leftX, int leftY, int rightX, int rightY) {
|
||||||
|
img = cropBitmap(img, leftX, leftY, rightX, rightY);
|
||||||
|
Point p = findPointByMulColor(img, colorRules);
|
||||||
|
if (p.isEmpty()) {
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
return new Point(p.getX() + leftX, p.getY() + leftY);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多色找点函数
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param colorRules
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Point findPointByMulColor(Bitmap img, String colorRules) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
int[] colors = new int[img.getWidth() * img.getHeight()];
|
||||||
|
String[] res = colorRules.split(",");
|
||||||
|
Color firstPointColor = HexColor2DecColor(res[0], true);
|
||||||
|
img.getPixels(colors, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
if (Color.isSame(new Color(colors[i]), firstPointColor)) {
|
||||||
|
int y = (int) (i / img.getWidth());
|
||||||
|
int x = i % img.getWidth();
|
||||||
|
for (int k = 1; k < res.length; k++) {
|
||||||
|
res[k] = res[k].replace("\"", "");
|
||||||
|
String[] info = res[k].split("\\|");
|
||||||
|
int testX = x + Integer.parseInt(info[0]);
|
||||||
|
int testY = y + Integer.parseInt(info[1]);
|
||||||
|
if (testX < 0 || testY < 0 || testX > img.getWidth() || testY > img.getHeight()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Color nextColor = getPoint(img, testX, testY);
|
||||||
|
if (!Color.isSame(nextColor, HexColor2DecColor(info[2], true))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (k == (res.length - 1)) {
|
||||||
|
LogUtils.i("找点用时:", String.valueOf(System.currentTimeMillis() - now));
|
||||||
|
return new Point(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return new Point(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多色找点,自定义颜色误差
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param colorRules
|
||||||
|
* @param offset
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Point findPointByMulColor(Bitmap img, String colorRules, int offset) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
// 将图像转换成颜色数组
|
||||||
|
int[] colors = new int[img.getWidth() * img.getHeight()];
|
||||||
|
String[] res = colorRules.split(",");
|
||||||
|
Color firstPointColor = HexColor2DecColor(res[0], true);
|
||||||
|
img.getPixels(colors, 0, img.getWidth(), 0, 0, img.getWidth(), img.getHeight());
|
||||||
|
//遍历颜色数组
|
||||||
|
for (int i = 0; i < colors.length; i++) {
|
||||||
|
// 寻找规则中第一个点
|
||||||
|
if (Color.isSame(new Color(colors[i]), firstPointColor, offset)) {
|
||||||
|
// 第一个点的y坐标
|
||||||
|
int y = (int) (i / img.getWidth());
|
||||||
|
// 第一个点的x坐标
|
||||||
|
int x = i % img.getWidth();
|
||||||
|
// 检查规则中后续每个点
|
||||||
|
for (int k = 1; k < res.length; k++) {
|
||||||
|
//处理规则中多余的引号
|
||||||
|
res[k] = res[k].replace("\"", "");
|
||||||
|
String[] info = res[k].split("\\|");
|
||||||
|
int testX = x + Integer.parseInt(info[0]);
|
||||||
|
int testY = y + Integer.parseInt(info[1]);
|
||||||
|
//超出图片范围
|
||||||
|
if (testX < 0 || testY < 0 || testX > img.getWidth() || testY > img.getHeight()) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Color nextColor = getPoint(img, testX, testY);
|
||||||
|
if (!Color.isSame(nextColor, HexColor2DecColor(info[2], true), offset)) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (k == (res.length - 1)) {
|
||||||
|
return new Point(x, y);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogUtils.i("找点用时:", String.valueOf(System.currentTimeMillis() - now));
|
||||||
|
return new Point(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 已废弃
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param colorRules
|
||||||
|
* @return
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public static Point findPointByMulColorBack(Bitmap img, String colorRules) {
|
||||||
|
long now = System.currentTimeMillis();
|
||||||
|
String[] res = colorRules.split(",");
|
||||||
|
Color firstPointColor = HexColor2DecColor(res[0], true);
|
||||||
|
int imgWidth = img.getWidth();
|
||||||
|
int imgHeight = img.getHeight();
|
||||||
|
for (int i = 0; i < imgWidth; i++) {
|
||||||
|
for (int j = 0; j < imgHeight; j++) {
|
||||||
|
if (Color.isSame(getPoint(img, i, j), firstPointColor)) {
|
||||||
|
for (int k = 1; k < res.length; k++) {
|
||||||
|
res[k] = res[k].replace("\"", "");
|
||||||
|
String[] info = res[k].split("\\|");
|
||||||
|
int testX = i + Integer.parseInt(info[0]);
|
||||||
|
int testY = j + Integer.parseInt(info[1]);
|
||||||
|
if (testX < 0 || testY < 0 || testX > imgWidth || testY > imgHeight) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Color nextColor = getPoint(img, testX, testY);
|
||||||
|
if (!Color.isSame(nextColor, HexColor2DecColor(info[2], true))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (k == (res.length - 1)) {
|
||||||
|
LogUtils.i("找点用时:", String.valueOf(System.currentTimeMillis() - now));
|
||||||
|
return new Point(i, j);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
LogUtils.i("找点用时:", String.valueOf(System.currentTimeMillis() - now));
|
||||||
|
return new Point(-1, -1);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 多色找点,返回屏幕内全部满足规则的点
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param colorRules
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static LinkedList<Point> findPointsByMulColor(Bitmap img, String colorRules) {
|
||||||
|
LinkedList<Point> ret = new LinkedList<Point>();
|
||||||
|
String[] res = colorRules.split(",");
|
||||||
|
Color firstPointColor = HexColor2DecColor(res[0], true);
|
||||||
|
int imgWidth = img.getWidth();
|
||||||
|
int imgHeight = img.getHeight();
|
||||||
|
|
||||||
|
for (int i = 0; i < imgWidth; i++) {
|
||||||
|
for (int j = 0; j < imgHeight; j++) {
|
||||||
|
if (Color.isSame(getPoint(img, i, j), firstPointColor)) {
|
||||||
|
for (int k = 1; k < res.length; k++) {
|
||||||
|
res[k] = res[k].replace("\"", "");
|
||||||
|
String[] info = res[k].split("\\|");
|
||||||
|
int testX = i + Integer.parseInt(info[0]);
|
||||||
|
int testY = j + Integer.parseInt(info[1]);
|
||||||
|
if (testX < 0 || testY < 0 || testX > imgWidth || testY > imgHeight) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
Color nextColor = getPoint(img, testX, testY);
|
||||||
|
if (!Color.isSame(nextColor, HexColor2DecColor(info[2], true))) {
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
if (k == (res.length - 1)) {
|
||||||
|
ret.add(new Point(i, j));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param color
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Color HexColor2DecColor(String color) {
|
||||||
|
color = color.replace("#", "");
|
||||||
|
color = color.replace("\"", "");
|
||||||
|
try {
|
||||||
|
int r = Integer.parseInt(color.substring(0, 2), 16);
|
||||||
|
int g = Integer.parseInt(color.substring(2, 4), 16);
|
||||||
|
int b = Integer.parseInt(color.substring(4, 6), 16);
|
||||||
|
return new Color(r, g, b);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new Color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param color
|
||||||
|
* @param bgr
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Color HexColor2DecColor(String color, boolean bgr) {
|
||||||
|
color = color.replace("#", "");
|
||||||
|
color = color.replace("\"", "");
|
||||||
|
try {
|
||||||
|
int b = Integer.parseInt(color.substring(0, 2), 16);
|
||||||
|
int g = Integer.parseInt(color.substring(2, 4), 16);
|
||||||
|
int r = Integer.parseInt(color.substring(4, 6), 16);
|
||||||
|
return new Color(r, g, b);
|
||||||
|
|
||||||
|
} catch (Exception e) {
|
||||||
|
return new Color();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取一个点的颜色
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param x
|
||||||
|
* @param y
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Color getPoint(Bitmap img, int x, int y) {
|
||||||
|
try {
|
||||||
|
return new Color(img.getPixel(x, y));
|
||||||
|
} catch (IllegalArgumentException e) {
|
||||||
|
return new Color(0, 0, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 预览图片
|
||||||
|
*
|
||||||
|
* @param img
|
||||||
|
* @param context
|
||||||
|
*/
|
||||||
|
// public static void show(Bitmap img, Context context) {
|
||||||
|
// Dialog dia = new Dialog(context, R.style.edit_AlertDialog_style2);
|
||||||
|
// dia.setContentView(R.layout.activity_start_dialog);
|
||||||
|
// ImageView imageView = (ImageView) dia.findViewById(R.id.start_img);
|
||||||
|
// imageView.setImageBitmap(img);
|
||||||
|
// dia.show();
|
||||||
|
//
|
||||||
|
// dia.setCanceledOnTouchOutside(true); // Sets whether this dialog is
|
||||||
|
// Window w = dia.getWindow();
|
||||||
|
// WindowManager.LayoutParams lp = w.getAttributes();
|
||||||
|
// lp.x = 0;
|
||||||
|
// lp.y = 40;
|
||||||
|
// dia.onWindowAttributesChanged(lp);
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 裁剪
|
||||||
|
*
|
||||||
|
* @param bitmap
|
||||||
|
* @param leftTopX
|
||||||
|
* @param leftTopY
|
||||||
|
* @param rightBottomX
|
||||||
|
* @param rightBottomY
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Bitmap cropBitmap(Bitmap bitmap, int leftTopX, int leftTopY, int rightBottomX, int rightBottomY) {
|
||||||
|
return Bitmap.createBitmap(bitmap, leftTopX, leftTopY, rightBottomX - leftTopX, rightBottomY - leftTopY, null, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* base64 图片
|
||||||
|
*
|
||||||
|
* @param bitmap
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static String encodeImage(Bitmap bitmap) {
|
||||||
|
ByteArrayOutputStream baos = new ByteArrayOutputStream();
|
||||||
|
|
||||||
|
//读取图片到ByteArrayOutputStream
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, baos); //参数如果为100那么就不压缩
|
||||||
|
byte[] bytes = baos.toByteArray();
|
||||||
|
|
||||||
|
return Base64.encodeToString(bytes, Base64.DEFAULT);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 模板匹配
|
||||||
|
*
|
||||||
|
* @param srcImg //源图像
|
||||||
|
* @param templateImg //模板图像
|
||||||
|
* @param threshold //相识度阈值,阈值调小可以一定程度解决不同手机分辨率的问题
|
||||||
|
* @return //如果没有找到则返回(-1,-1)点
|
||||||
|
*/
|
||||||
|
// public static Point matchTemplate(Bitmap srcImg, Bitmap templateImg, double threshold) {
|
||||||
|
//
|
||||||
|
// if (threshold <= 0) {
|
||||||
|
// threshold = 0.5;
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// Mat tpl = new Mat();
|
||||||
|
// Mat src = new Mat();
|
||||||
|
// Utils.bitmapToMat(srcImg, src);
|
||||||
|
// Utils.bitmapToMat(templateImg, tpl);
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// int height = src.rows() - tpl.rows() + 1;
|
||||||
|
// int width = src.cols() - tpl.cols() + 1;
|
||||||
|
// Mat result = new Mat(height, width, CvType.CV_32FC1);
|
||||||
|
// int method = Imgproc.TM_CCOEFF_NORMED;
|
||||||
|
// Imgproc.matchTemplate(src, tpl, result, method);
|
||||||
|
// Core.MinMaxLocResult minMaxResult = Core.minMaxLoc(result);
|
||||||
|
// org.opencv.core.Point maxloc = minMaxResult.maxLoc;
|
||||||
|
// if (minMaxResult.maxVal < threshold) {
|
||||||
|
// return new Point(-1, -1);
|
||||||
|
// }
|
||||||
|
// org.opencv.core.Point minloc = minMaxResult.minLoc;
|
||||||
|
// org.opencv.core.Point matchloc = null;
|
||||||
|
// matchloc = maxloc;
|
||||||
|
// return new Point((int) matchloc.x, (int) matchloc.y);
|
||||||
|
//
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据给定的宽和高进行resize
|
||||||
|
*
|
||||||
|
* @param origin 原图
|
||||||
|
* @param newWidth 新图的宽
|
||||||
|
* @param newHeight 新图的高
|
||||||
|
* @return new Bitmap
|
||||||
|
*/
|
||||||
|
public static Bitmap resize(Bitmap origin, int newWidth, int newHeight) {
|
||||||
|
if (origin == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
int height = origin.getHeight();
|
||||||
|
int width = origin.getWidth();
|
||||||
|
float scaleWidth = ((float) newWidth) / width;
|
||||||
|
float scaleHeight = ((float) newHeight) / height;
|
||||||
|
Matrix matrix = new Matrix();
|
||||||
|
matrix.postScale(scaleWidth, scaleHeight);// 使用后乘
|
||||||
|
Bitmap newBM = Bitmap.createBitmap(origin, 0, 0, width, height, matrix, false);
|
||||||
|
if (!origin.isRecycled()) {
|
||||||
|
origin.recycle();
|
||||||
|
}
|
||||||
|
return newBM;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
package org.yameida.worktool.utils.capture
|
||||||
|
|
||||||
|
import android.media.projection.MediaProjection
|
||||||
|
import android.os.Handler
|
||||||
|
import android.os.Looper
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Created by Gallon on 2019/8/4.
|
||||||
|
*/
|
||||||
|
class MediaProjectionHolder {
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
var mMediaProjection: MediaProjection? = null
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
|
fun setMediaProjection(mediaProjection: MediaProjection) {
|
||||||
|
if (mMediaProjection == null) {
|
||||||
|
mMediaProjection = mediaProjection
|
||||||
|
mediaProjection.registerCallback(object : MediaProjection.Callback() {
|
||||||
|
override fun onStop() {
|
||||||
|
mMediaProjection = null
|
||||||
|
}
|
||||||
|
}, Handler(Looper.getMainLooper()))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
package org.yameida.worktool.utils.capture;
|
||||||
|
|
||||||
|
public class Point {
|
||||||
|
|
||||||
|
private int x = 0;
|
||||||
|
private int y = 0;
|
||||||
|
|
||||||
|
public Point(int x, int y) {
|
||||||
|
this.x = x;
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Point() {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public int getX() {
|
||||||
|
return x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setX(int x) {
|
||||||
|
this.x = x;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int getY() {
|
||||||
|
return y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setY(int y) {
|
||||||
|
this.y = y;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isEmpty() {
|
||||||
|
return x < 0 || y < 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String toString() {
|
||||||
|
return "{" + x + "," + y + "}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,119 @@
|
|||||||
|
package org.yameida.worktool.utils.capture;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.res.Configuration;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.LogUtils;
|
||||||
|
import com.blankj.utilcode.util.Utils;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截图工具类
|
||||||
|
*/
|
||||||
|
public class ScreenCaptureUtil {
|
||||||
|
|
||||||
|
private static int screenOrientation; // 0 未设置 ,1竖屏 2横批
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制设置为横屏模式
|
||||||
|
*/
|
||||||
|
public static void setHorizontalScreen() {
|
||||||
|
ScreenCaptureUtil.screenOrientation = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 强制设置为竖屏模式
|
||||||
|
*/
|
||||||
|
public static void setVerticalScreen() {
|
||||||
|
ScreenCaptureUtil.screenOrientation = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 获取屏幕图像
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Bitmap getScreenCap() {
|
||||||
|
Bitmap o_img;
|
||||||
|
boolean retry = false;
|
||||||
|
do {
|
||||||
|
if (retry) {
|
||||||
|
LogUtils.i("资源已被回收,尝试重试!");
|
||||||
|
}
|
||||||
|
if (ScreenCaptureUtil.screenOrientation == 0) {
|
||||||
|
if (Utils.getApp().getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
|
||||||
|
//竖屏
|
||||||
|
o_img = ScreenCaptureUtilByMediaPro.getScreenCapVertical();
|
||||||
|
} else {
|
||||||
|
//横屏
|
||||||
|
o_img = ScreenCaptureUtilByMediaPro.getScreenCapHorizontal();
|
||||||
|
}
|
||||||
|
} else if (ScreenCaptureUtil.screenOrientation == 1) {
|
||||||
|
o_img = ScreenCaptureUtilByMediaPro.getScreenCapVertical();
|
||||||
|
} else {
|
||||||
|
o_img = ScreenCaptureUtilByMediaPro.getScreenCapHorizontal();
|
||||||
|
}
|
||||||
|
retry = true;
|
||||||
|
} while (o_img == null || o_img.isRecycled());
|
||||||
|
|
||||||
|
|
||||||
|
return Bitmap.createBitmap(o_img);
|
||||||
|
|
||||||
|
|
||||||
|
// 使用adb方式截图,性能低下,已废弃
|
||||||
|
// if (System.currentTimeMillis() - getScreenTime < 1000) {
|
||||||
|
// return screenCache;
|
||||||
|
// }
|
||||||
|
// byte[] tempBuffer = new byte[100 * 1024 * 1024];
|
||||||
|
// StringBuilder buffer = new StringBuilder(100 * 1024 * 1024);
|
||||||
|
//
|
||||||
|
// Process exec = null;
|
||||||
|
// try {
|
||||||
|
// exec = Runtime.getRuntime().exec("su -c /system/bin/screencap -p");
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// final InputStream inputStream = exec.getInputStream();
|
||||||
|
// BufferedInputStream bufferedInputStream = new BufferedInputStream(inputStream);
|
||||||
|
// //清空缓存内容
|
||||||
|
// buffer.setLength(0);
|
||||||
|
// int count;
|
||||||
|
// while ((count = bufferedInputStream.read(tempBuffer)) > 0) {
|
||||||
|
// buffer.append(new String(tempBuffer, 0, count, StandardCharsets.ISO_8859_1));
|
||||||
|
// }
|
||||||
|
// bufferedInputStream.close();
|
||||||
|
// final int retCode = exec.waitFor();
|
||||||
|
// exec.destroy();
|
||||||
|
// tempBuffer = buffer.toString().getBytes(StandardCharsets.ISO_8859_1);
|
||||||
|
// screenCache = BitmapFactory.decodeByteArray(tempBuffer, 0, tempBuffer.length);
|
||||||
|
// getScreenTime = System.currentTimeMillis();
|
||||||
|
// return screenCache;
|
||||||
|
// } catch (IOException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// } catch (InterruptedException e) {
|
||||||
|
// e.printStackTrace();
|
||||||
|
// }
|
||||||
|
// throw new NullPointerException("截图失败");
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 指定范围截图
|
||||||
|
*
|
||||||
|
* @param leftX
|
||||||
|
* @param leftY
|
||||||
|
* @param rigthX
|
||||||
|
* @param rightY
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
public static Bitmap getScreenCap(int leftX, int leftY, int rigthX, int rightY) {
|
||||||
|
Bitmap bitmap = getScreenCap();
|
||||||
|
return Image.cropBitmap(bitmap, leftX, leftY, rigthX, rightY);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,142 @@
|
|||||||
|
package org.yameida.worktool.utils.capture;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.graphics.PixelFormat;
|
||||||
|
import android.hardware.display.DisplayManager;
|
||||||
|
import android.media.ImageReader;
|
||||||
|
import android.media.projection.MediaProjection;
|
||||||
|
import android.media.projection.MediaProjectionManager;
|
||||||
|
import android.os.Handler;
|
||||||
|
|
||||||
|
import com.blankj.utilcode.util.ScreenUtils;
|
||||||
|
|
||||||
|
import java.nio.ByteBuffer;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 截图的底层实现类
|
||||||
|
* 这个类的方法不要在业务中直接调用
|
||||||
|
* 业务中使用ScreenCaptureUtil类
|
||||||
|
*
|
||||||
|
* @deprecated
|
||||||
|
*/
|
||||||
|
public class ScreenCaptureUtilByMediaPro {
|
||||||
|
public static MediaProjectionManager mProjectionManager;
|
||||||
|
public static Intent data;
|
||||||
|
public static int resultCode;
|
||||||
|
private static MediaProjection sMediaProjection;
|
||||||
|
private static ImageReader mImageReaderHorizontal;
|
||||||
|
private static ImageReader mImageReaderVertical;
|
||||||
|
private static Handler backgroundHandler;
|
||||||
|
|
||||||
|
private static Bitmap bitmapCacheHorizontal;
|
||||||
|
private static Bitmap bitmapCacheVertical;
|
||||||
|
|
||||||
|
public static void init() {
|
||||||
|
sMediaProjection = mProjectionManager.getMediaProjection(resultCode, data);
|
||||||
|
//start capture reader
|
||||||
|
mImageReaderHorizontal = ImageReader.newInstance(ScreenUtils.getScreenWidth(), ScreenUtils.getScreenHeight(),
|
||||||
|
PixelFormat.RGBA_8888, 2);
|
||||||
|
sMediaProjection.createVirtualDisplay(
|
||||||
|
"ScreenShot",
|
||||||
|
ScreenUtils.getScreenWidth(),
|
||||||
|
ScreenUtils.getScreenHeight(),
|
||||||
|
ScreenUtils.getScreenDensityDpi(),
|
||||||
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
|
||||||
|
mImageReaderHorizontal.getSurface(),
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
|
||||||
|
mImageReaderVertical = ImageReader.newInstance(ScreenUtils.getScreenHeight(), ScreenUtils.getScreenWidth(),
|
||||||
|
PixelFormat.RGBA_8888, 2);
|
||||||
|
sMediaProjection.createVirtualDisplay(
|
||||||
|
"ScreenShot",
|
||||||
|
ScreenUtils.getScreenHeight(),
|
||||||
|
ScreenUtils.getScreenWidth(),
|
||||||
|
ScreenUtils.getScreenDensityDpi(),
|
||||||
|
DisplayManager.VIRTUAL_DISPLAY_FLAG_OWN_CONTENT_ONLY | DisplayManager.VIRTUAL_DISPLAY_FLAG_PUBLIC,
|
||||||
|
mImageReaderVertical.getSurface(),
|
||||||
|
null,
|
||||||
|
null);
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 勿直接使用!!
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Bitmap getScreenCapHorizontal() {
|
||||||
|
|
||||||
|
Bitmap bitmap;
|
||||||
|
android.media.Image image;
|
||||||
|
do {
|
||||||
|
image = mImageReaderHorizontal.acquireLatestImage();
|
||||||
|
if (image == null && bitmapCacheHorizontal != null) {
|
||||||
|
return bitmapCacheHorizontal;
|
||||||
|
}
|
||||||
|
} while (image == null);
|
||||||
|
bitmap = covetBitmap(image);
|
||||||
|
if (bitmapCacheHorizontal != null && !bitmapCacheHorizontal.isRecycled()) {
|
||||||
|
bitmapCacheHorizontal.recycle();
|
||||||
|
bitmapCacheHorizontal = null;
|
||||||
|
}
|
||||||
|
bitmapCacheHorizontal = bitmap;
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 勿直接使用!!
|
||||||
|
*
|
||||||
|
* @return
|
||||||
|
*/
|
||||||
|
@Deprecated
|
||||||
|
public static Bitmap getScreenCapVertical() {
|
||||||
|
|
||||||
|
Bitmap bitmap;
|
||||||
|
android.media.Image image;
|
||||||
|
do {
|
||||||
|
image = mImageReaderVertical.acquireLatestImage();
|
||||||
|
if (image == null && bitmapCacheVertical != null) {
|
||||||
|
return bitmapCacheVertical;
|
||||||
|
}
|
||||||
|
} while (image == null);
|
||||||
|
bitmap = covetBitmap(image);
|
||||||
|
if (bitmapCacheVertical != null) {
|
||||||
|
bitmapCacheVertical.recycle();
|
||||||
|
bitmapCacheVertical = null;
|
||||||
|
}
|
||||||
|
bitmapCacheVertical = bitmap;
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static Bitmap covetBitmap(android.media.Image image) {
|
||||||
|
int width = image.getWidth();
|
||||||
|
int height = image.getHeight();
|
||||||
|
final android.media.Image.Plane[] planes = image.getPlanes();
|
||||||
|
final ByteBuffer buffer = planes[0].getBuffer();
|
||||||
|
//每个像素的间距
|
||||||
|
int pixelStride = planes[0].getPixelStride();
|
||||||
|
//总的间距
|
||||||
|
int rowStride = planes[0].getRowStride();
|
||||||
|
int rowPadding = rowStride - pixelStride * width;
|
||||||
|
Bitmap bitmap = Bitmap.createBitmap(width + rowPadding / pixelStride, height, Bitmap.Config.ARGB_8888);
|
||||||
|
bitmap.copyPixelsFromBuffer(buffer);
|
||||||
|
bitmap = Bitmap.createBitmap(bitmap, 0, 0, width, height);
|
||||||
|
image.close();
|
||||||
|
return bitmap;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static void stop() {
|
||||||
|
sMediaProjection.stop();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -61,4 +61,30 @@
|
|||||||
</item>
|
</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="AppThemeQmui" parent="QMUI.Compat.NoActionBar">
|
||||||
|
<!-- Customize your theme here. -->
|
||||||
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||||
|
<item name="colorAccent">@color/float_time_color</item>
|
||||||
|
</style>
|
||||||
|
<style name="AppTheme.TranslucentNoTitleFullscreen" parent="@style/AppThemeQmui">
|
||||||
|
<item name="android:windowBackground">
|
||||||
|
@color/transparent</item>
|
||||||
|
<item name="android:windowNoTitle">
|
||||||
|
true</item>
|
||||||
|
<item name="android:windowIsTranslucent">
|
||||||
|
true</item>
|
||||||
|
<item name="android:windowContentOverlay">
|
||||||
|
@null</item>
|
||||||
|
<item name="android:windowAnimationStyle">
|
||||||
|
@null</item>
|
||||||
|
<item name="android:windowFullscreen">
|
||||||
|
false</item>
|
||||||
|
<item name="android:colorBackgroundCacheHint">
|
||||||
|
@null</item>
|
||||||
|
<item name="android:windowDisablePreview">
|
||||||
|
true
|
||||||
|
</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
Reference in New Issue
Block a user