update 图像识别截图和前台服务

This commit is contained in:
gallonyin
2023-02-20 17:47:27 +08:00
parent 8c1fa8d9c9
commit e8a2dbeed1
19 changed files with 1550 additions and 1 deletions

View File

@@ -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"

View File

@@ -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) {

View File

@@ -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())
} }

View File

@@ -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()
}
}

View File

@@ -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()) {

View File

@@ -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()
}
}
}

View File

@@ -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)
}
}
/** /**
* 进入首页-消息页 * 进入首页-消息页
*/ */

View File

@@ -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)
}
}
}
}

View File

@@ -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])
} }

View File

@@ -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
}

View File

@@ -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
}
}
}

View File

@@ -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)
}
}

View 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;
}
}

View 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;
}
}

View File

@@ -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()))
}
}
}
}

View File

@@ -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 + "}";
}
}

View File

@@ -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);
}
}

View File

@@ -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();
}
}

View File

@@ -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>