diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index c7a5d5c..e109f16 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -70,6 +70,12 @@
android:windowSoftInputMode="adjustUnspecified|stateHidden"
android:theme="@style/AppTheme">
+
+
, 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)
+ }
+}
+
/**
* 进入首页-消息页
*/
diff --git a/app/src/main/java/org/yameida/worktool/service/PlayNotifyService.kt b/app/src/main/java/org/yameida/worktool/service/PlayNotifyService.kt
new file mode 100644
index 0000000..5e89168
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/service/PlayNotifyService.kt
@@ -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("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)
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt b/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt
index 179b6fb..cd3ba01 100644
--- a/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt
+++ b/app/src/main/java/org/yameida/worktool/service/WeworkGetImpl.kt
@@ -256,7 +256,7 @@ object WeworkGetImpl {
for (i in 0 until list.childCount) {
val item = list.getChild(i)
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()) {
corpList.add(textList[0])
}
diff --git a/app/src/main/java/org/yameida/worktool/utils/ExtensionMethod.kt b/app/src/main/java/org/yameida/worktool/utils/ExtensionMethod.kt
new file mode 100644
index 0000000..ac6049f
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/ExtensionMethod.kt
@@ -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
+}
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/AndroidUtils.kt b/app/src/main/java/org/yameida/worktool/utils/capture/AndroidUtils.kt
new file mode 100644
index 0000000..74c9c48
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/AndroidUtils.kt
@@ -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(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
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/BitmapUtil.kt b/app/src/main/java/org/yameida/worktool/utils/capture/BitmapUtil.kt
new file mode 100644
index 0000000..fd0ca16
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/BitmapUtil.kt
@@ -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)
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/Color.java b/app/src/main/java/org/yameida/worktool/utils/capture/Color.java
new file mode 100644
index 0000000..d4f18d9
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/Color.java
@@ -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;
+ }
+}
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/Image.java b/app/src/main/java/org/yameida/worktool/utils/capture/Image.java
new file mode 100644
index 0000000..728472d
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/Image.java
@@ -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 findPoint(Bitmap img, Color color) {
+ LinkedList pl = new LinkedList();
+ 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 findPointsByMulColor(Bitmap img, String colorRules) {
+ LinkedList ret = new LinkedList();
+ 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;
+ }
+
+}
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/MediaProjectionHolder.kt b/app/src/main/java/org/yameida/worktool/utils/capture/MediaProjectionHolder.kt
new file mode 100644
index 0000000..2974e68
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/MediaProjectionHolder.kt
@@ -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()))
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/Point.java b/app/src/main/java/org/yameida/worktool/utils/capture/Point.java
new file mode 100644
index 0000000..3a2079f
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/Point.java
@@ -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 + "}";
+ }
+}
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtil.java b/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtil.java
new file mode 100644
index 0000000..72aa0cb
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtil.java
@@ -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);
+ }
+
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtilByMediaPro.java b/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtilByMediaPro.java
new file mode 100644
index 0000000..b1f85fc
--- /dev/null
+++ b/app/src/main/java/org/yameida/worktool/utils/capture/ScreenCaptureUtilByMediaPro.java
@@ -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();
+ }
+}
diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml
index c4aec85..5e5aa8a 100644
--- a/app/src/main/res/values/styles.xml
+++ b/app/src/main/res/values/styles.xml
@@ -61,4 +61,30 @@
+
+
+
\ No newline at end of file