Skip to content

Commit

Permalink
improve stability
Browse files Browse the repository at this point in the history
  • Loading branch information
xuduo committed Dec 11, 2023
1 parent 29c4483 commit f85a950
Show file tree
Hide file tree
Showing 8 changed files with 66 additions and 89 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import android.view.accessibility.AccessibilityNodeInfo
import androidx.core.graphics.toRect
import com.xd.common.logger.Logger
import com.xd.testrecorder.overlay.OverlayService
import com.xd.testrecorder.service.serviceFailed
import java.text.SimpleDateFormat
import java.util.Date
import java.util.Locale
Expand All @@ -25,10 +26,6 @@ class TouchAccessibilityService : AccessibilityService() {
companion object {
var service: TouchAccessibilityService? = null

fun isTargetPackage(): Boolean {
return service?.recordingPackageName == service?.foregroundPackageName
}

fun getStatusBarHeight(): Int {
return service?.getStatusBarHeight() ?: 0
}
Expand Down Expand Up @@ -69,10 +66,10 @@ class TouchAccessibilityService : AccessibilityService() {

override fun onDestroy() {
super.onDestroy()
logger.d("onDestroy")
logger.i("onDestroy")
service = null
recordingPackageName = "not.recording"
OverlayService.service?.adjustPassThrough()
OverlayService.service?.serviceFailed()
}

override fun onAccessibilityEvent(event: AccessibilityEvent?) {
Expand All @@ -83,23 +80,6 @@ class TouchAccessibilityService : AccessibilityService() {
)
}"
)
if (event.eventType == AccessibilityEvent.TYPE_TOUCH_INTERACTION_START) {
// タッチイベントが開始した時の処理
logger.d("TYPE_TOUCH_INTERACTION_START $event")
}

if (event.eventType == AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED) {
if (event.packageName != null) {
foregroundPackageName = event.packageName.toString()
}
logger.i("TYPE_WINDOW_STATE_CHANGED $foregroundPackageName")
OverlayService.service?.adjustPassThrough()
}

if (event.eventType == AccessibilityEvent.TYPE_TOUCH_INTERACTION_END) {
// タッチイベントが終了した時の処理
logger.d("TYPE_TOUCH_INTERACTION_END $event")
}
}

fun getViewForGesture(gesture: GestureDescription?): Pair<AccessibilityNodeInfo?, AccessibilityNodeInfo?> {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ sealed class ActionCodeConverter {
code = "clickContentDescription(\"${action.viewContentDescription}\")"
} else if (action.viewText.isNotEmpty()) {
code = "clickText(\"${action.viewText}\")"
} else{
code = "// no ContentDescription or Text detected"
}
return code
}
Expand Down
9 changes: 1 addition & 8 deletions model/src/main/java/com/xd/testrecorder/data/ActionImage.kt
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ data class ActionImage(
)

fun convertImageToByteArray(image: Image, cropTop: Int = 0, cropHeight: Int = 0): ByteArray {
Log.d("convertImageToByteArray", "convertImageToByteArray ${image.width} ${image.height}")
Log.d("convertImageToByteArray", "convertImageToByteArray ${image.width} ${image.height} $cropTop")
val originalBitmap = Bitmap.createBitmap(image.width, image.height, Bitmap.Config.ARGB_8888)
originalBitmap.copyPixelsFromBuffer(image.planes[0].buffer)

Expand Down Expand Up @@ -60,10 +60,3 @@ fun convertImageToByteArray(image: Image, cropTop: Int = 0, cropHeight: Int = 0)

return byteArray
}

private fun getBitmapFromRGBA(bytes: ByteArray, width: Int, height: Int): Bitmap {
val bitmap = Bitmap.createBitmap(width, height, Bitmap.Config.ARGB_8888)
val buffer = ByteBuffer.wrap(bytes)
bitmap.copyPixelsFromBuffer(buffer)
return bitmap
}
85 changes: 37 additions & 48 deletions model/src/main/java/com/xd/testrecorder/overlay/OverlayService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package com.xd.testrecorder.overlay

import android.accessibilityservice.AccessibilityService.GestureResultCallback
import android.accessibilityservice.GestureDescription
import android.annotation.SuppressLint
import android.app.Service
import android.content.Intent
import android.gesture.GestureOverlayView
Expand All @@ -23,6 +24,7 @@ import com.xd.testrecorder.data.ActionImage
import com.xd.testrecorder.data.convertImageToByteArray
import com.xd.testrecorder.data.convertMotionEventsToAction
import com.xd.testrecorder.recorder.RecorderService
import com.xd.testrecorder.service.serviceFailed
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand All @@ -33,6 +35,7 @@ import javax.inject.Inject
class OverlayService : Service() {

companion object {
@SuppressLint("StaticFieldLeak")
var service: OverlayService? = null
}

Expand All @@ -46,11 +49,7 @@ class OverlayService : Service() {

private lateinit var windowManager: WindowManager
private lateinit var overlayView: View
private val type: Int = if (Build.VERSION.SDK_INT < Build.VERSION_CODES.O) {
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT
} else {
WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
}
private val type: Int = WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY
private val flagsPassThrough = WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS or
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE or
WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
Expand Down Expand Up @@ -100,9 +99,10 @@ class OverlayService : Service() {
// overlayView.setBackgroundColor(Color.parseColor("#60FF0000"))

// Set OnTouchListener

overlayView.setOnTouchListener { view, event ->
// Handle touch events here
logger.d("OnTouchListener $event ${isPassThrough()}")
logger.d("OnTouchListener $event")
if (isPassThrough()) {
return@setOnTouchListener false
}
Expand All @@ -127,12 +127,7 @@ class OverlayService : Service() {
params.gravity = Gravity.TOP or Gravity.START
params.x = 0;
params.y = 0;

if (TouchAccessibilityService.isTargetPackage()) {
params.flags = flagsCapture
} else {
params.flags = flagsPassThrough
}
params.flags = flagsCapture

overlayView.addOnAttachStateChangeListener(object : View.OnAttachStateChangeListener {
override fun onViewAttachedToWindow(v: View) {
Expand All @@ -149,6 +144,9 @@ class OverlayService : Service() {
}

private fun handleMotionActionUp() {
if (serviceFailed()) {
return
}
changeToPassThrough()
if (recordingId < 0) {
logger.e("recordingId less than 0")
Expand Down Expand Up @@ -190,32 +188,34 @@ class OverlayService : Service() {
)
action.clickableViewClassName = node.className?.toString() ?: ""
action.featureViewClassName = node.className?.toString() ?: ""
}
val feature = pair.second
if (feature != null && feature != node) {
action.viewContentDescription = feature.contentDescription?.toString() ?: ""
action.viewText = feature.text?.toString() ?: ""
val rect = Rect()
feature.getBoundsInScreen(rect)
action.featureViewBounds = Rect(
rect.left,
rect.top - TouchAccessibilityService.getStatusBarHeight(),
rect.right,
rect.bottom - TouchAccessibilityService.getStatusBarHeight()
val feature = pair.second
if (feature != null && feature != node) {
action.viewContentDescription = feature.contentDescription?.toString() ?: ""
action.viewText = feature.text?.toString() ?: ""
val rect = Rect()
feature.getBoundsInScreen(rect)
action.featureViewBounds = Rect(
rect.left,
rect.top - TouchAccessibilityService.getStatusBarHeight(),
rect.right,
rect.bottom - TouchAccessibilityService.getStatusBarHeight()
)
action.featureViewClassName = feature.className?.toString() ?: ""
}
val actionId = actionDao.insertAction(action)
val actionImage = ActionImage(
actionId = actionId,
screenShot = convertImageToByteArray(
image,
TouchAccessibilityService.getStatusBarHeight(),
overlayView.height
)
)
action.featureViewClassName = feature.className?.toString() ?: ""
actionImageDao.insertActionImage(actionImage)
logger.i("insert action success $action")
} else {
logger.i("not target package skip insert")
}
val actionId = actionDao.insertAction(action)
val actionImage = ActionImage(
actionId = actionId,
screenShot = convertImageToByteArray(
image,
TouchAccessibilityService.getStatusBarHeight(),
overlayView.height
)
)
actionImageDao.insertActionImage(actionImage)
logger.i("insert action success $action")
delay(100)
withContext(Dispatchers.Main) {
TouchAccessibilityService.dispatchGesture(
Expand All @@ -227,17 +227,6 @@ class OverlayService : Service() {
}
}

fun adjustPassThrough() {
if (TouchAccessibilityService.isTargetPackage()) {
changeToCapture()
} else {
changeToPassThrough()
}
if (TouchAccessibilityService.service == null) {
stopSelf()
}
}

private fun copyMotionEvent(originalEvent: MotionEvent): MotionEvent {
return MotionEvent.obtain(
originalEvent.downTime,
Expand Down Expand Up @@ -266,7 +255,7 @@ class OverlayService : Service() {

override fun onDestroy() {
super.onDestroy()
logger.d("onDestroy")
logger.i("onDestroy")
service = null
removeOverlay()
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import androidx.core.content.ContextCompat
import com.xd.common.logger.Logger
import com.xd.testrecorder.accessibility.TouchAccessibilityService
import com.xd.testrecorder.model.R
import com.xd.testrecorder.service.serviceFailed
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.delay
Expand Down Expand Up @@ -73,6 +74,10 @@ class RecorderService : Service() {
logger.i("RecorderService onCreate")
service = this
CoroutineScope(Dispatchers.Main).launch {
if (serviceFailed()) {
stopSelf()
return@launch
}
delay(1000L)
startRecording()
}
Expand Down Expand Up @@ -149,13 +154,6 @@ class RecorderService : Service() {
latestImage = null
}

private fun convertNanosecondsToReadable(timestamp: Long): String {
val instant = Instant.ofEpochSecond(0, timestamp)
val formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS").withZone(ZoneId.systemDefault())
return formatter.format(instant)
}

private fun createNotification(): Notification {
val notificationChannelId = "media_projection_service_channel"

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -90,8 +90,8 @@ class RecordingViewModel @Inject constructor(
val lastTimeUsed = lastTimeUsedMap[appInfo.packageName] ?: 0
val intent =
context.packageManager.getLaunchIntentForPackage(appInfo.packageName)
logger.v("getLaunchIntentForPackage ${appInfo.packageName} ${intent != null} ${iconBitmap != null} ${iconDrawable != null}")
if (iconBitmap != null && intent != null) {
logger.v("getLaunchIntentForPackage ${appInfo.packageName} ${intent != null} ${iconBitmap != null}")
if (iconBitmap != null && intent != null && context.packageName != appInfo.packageName) {
AppInfo(iconBitmap, appInfo.packageName, appName, lastTimeUsed)
} else null
} catch (e: PackageManager.NameNotFoundException) {
Expand Down
15 changes: 15 additions & 0 deletions model/src/main/java/com/xd/testrecorder/service/BaseService.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.xd.testrecorder.service

import android.app.Service
import com.xd.testrecorder.accessibility.TouchAccessibilityService
import com.xd.testrecorder.overlay.OverlayService
import com.xd.testrecorder.recorder.RecorderService

fun Service.serviceFailed(): Boolean {
return if(TouchAccessibilityService.service == null || OverlayService.service == null || RecorderService.service == null){
stopSelf()
true
} else {
false
}
}
2 changes: 1 addition & 1 deletion model/src/main/res/xml/accessibility_service_config.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<accessibility-service xmlns:android="http://schemas.android.com/apk/res/android"
android:accessibilityEventTypes="typeWindowContentChanged|typeWindowStateChanged"
android:accessibilityEventTypes=""
android:accessibilityFlags="flagDefault"
android:accessibilityFeedbackType="feedbackAllMask"
android:canPerformGestures="true"
Expand Down

0 comments on commit f85a950

Please sign in to comment.