Skip to content

Commit

Permalink
Merge pull request #205 from GuoXiCheng/dev-c
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
GuoXiCheng authored Aug 23, 2024
2 parents 1d1bf4e + bbb2996 commit 7e02e24
Show file tree
Hide file tree
Showing 12 changed files with 274 additions and 183 deletions.
8 changes: 8 additions & 0 deletions app/src/main/java/com/android/skip/NewMainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,11 @@ import com.android.skip.manager.SkipConfigManagerV2
import com.android.skip.manager.WhitelistManager
import com.android.skip.utils.AccessibilityUtils
import com.android.skip.utils.DataStoreUtils
import com.android.skip.utils.InstalledAppUtils
import com.android.skip.viewmodel.StartButtonViewModel
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import org.yaml.snakeyaml.Yaml
import java.io.File
import kotlin.concurrent.thread
Expand Down Expand Up @@ -163,6 +167,10 @@ class NewMainActivity : BaseActivity() {
}
}
}

CoroutineScope(Dispatchers.IO).launch {
InstalledAppUtils.updateInstalledAppsCache()
}
}
}

23 changes: 9 additions & 14 deletions app/src/main/java/com/android/skip/WhitelistActivity.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
package com.android.skip

import android.content.pm.ApplicationInfo
import android.content.pm.PackageManager
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.material3.Icon
import androidx.compose.runtime.Composable
Expand All @@ -19,6 +17,7 @@ import com.android.skip.compose.ScaffoldPage
import com.android.skip.dataclass.AppInfo
import com.android.skip.manager.WhitelistManager
import com.android.skip.utils.DataStoreUtils
import com.android.skip.utils.InstalledAppUtils
import com.google.accompanist.drawablepainter.rememberDrawablePainter
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
Expand All @@ -38,27 +37,23 @@ class WhitelistActivity : BaseActivity() {
@Composable
fun WhitelistInterface(onBackClick: () -> Unit) {
val context = LocalContext.current
val packageManager = context.packageManager
val appInfoList = remember { mutableStateListOf<AppInfo>() }
val scope = rememberCoroutineScope()

LaunchedEffect(Unit) {
scope.launch(Dispatchers.IO) {
var installedApps =
packageManager.getInstalledApplications(PackageManager.GET_META_DATA)

// 是否过滤掉系统应用
if (!DataStoreUtils.getSyncData(SKIP_INCLUDE_SYSTEM_APPS, false)) {
installedApps = installedApps.filter {
(it.flags and ApplicationInfo.FLAG_SYSTEM) == 0
}
}
val installedApps = InstalledAppUtils.getCachedApps(
DataStoreUtils.getSyncData(
SKIP_INCLUDE_SYSTEM_APPS,
false
)
)

val apps = installedApps.map { app ->
AppInfo(
appName = app.loadLabel(packageManager).toString(),
appName = app.name,
packageName = app.packageName,
appIcon = app.loadIcon(packageManager),
appIcon = app.icon,
checked = mutableStateOf(
DataStoreUtils.getSyncData(
WHITELIST_DOT + app.packageName,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.android.skip.dataclass

import android.view.accessibility.AccessibilityNodeInfo

data class AccessibilityNodeInfoCarrier(
val node: AccessibilityNodeInfo,
val depth: Int,
val parentId: Int,
val nodeId: Int
)
15 changes: 15 additions & 0 deletions app/src/main/java/com/android/skip/dataclass/NodeChildSchema.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.android.skip.dataclass

data class NodeChildSchema(
val depth: Int,
val childCount: Int,
val parentId: Int,
val nodeId: Int,
val left: Int,
val top: Int,
val right: Int,
val bottom: Int,
var className: String? = null,
var text: String? = null,
var viewIdResourceName: String? = null
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.android.skip.dataclass

data class NodeRootSchema(
val packageName: String,
val className: String,
val screenHeight: Int,
val screenWidth: Int,
val nodes: MutableList<NodeChildSchema>
)
85 changes: 5 additions & 80 deletions app/src/main/java/com/android/skip/service/LayoutInspectService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,8 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.graphics.Bitmap
import android.graphics.BitmapFactory
import android.graphics.PixelFormat
import android.hardware.display.DisplayManager
Expand All @@ -19,38 +16,21 @@ import android.media.ImageReader
import android.media.projection.MediaProjection
import android.media.projection.MediaProjectionManager
import android.os.Build
import android.os.Handler
import android.os.IBinder
import android.os.Looper
import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import com.android.skip.NewMainActivity
import com.android.skip.R
import com.android.skip.SKIPApp
import com.android.skip.SKIP_LAYOUT_INSPECT
import com.android.skip.manager.ToastManager
import com.android.skip.utils.Constants
import com.android.skip.utils.DataStoreUtils
import com.android.skip.utils.LayoutInspectUtils
import com.blankj.utilcode.util.ScreenUtils
import java.io.File
import java.io.FileOutputStream
import java.io.IOException


class LayoutInspectService: Service() {
private var mMediaProjection: MediaProjection? = null
private var mProjectionManager:MediaProjectionManager? = null
private var virtualDisplay: VirtualDisplay? = null
private var isProcessingImage = false
private var filename: String? = null
private val keyEventVolumeDownReceiver = object: BroadcastReceiver() {
override fun onReceive(p0: Context?, p1: Intent?) {
if (p1?.action == Constants.SKIP_KEY_EVENT_VOLUME_DOWN) {
isProcessingImage = true
filename = p1.getStringExtra("filename")
}
}
}

override fun onBind(p0: Intent?): IBinder? {
TODO("Not yet implemented")
Expand All @@ -63,22 +43,18 @@ class LayoutInspectService: Service() {
// 开启前台服务
val manager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel("layout_inspect_service", "前台布局检查服务通知", NotificationManager.IMPORTANCE_DEFAULT)
val channel = NotificationChannel("layout_inspect_service", "布局检查服务", NotificationManager.IMPORTANCE_DEFAULT)
manager.createNotificationChannel(channel)
}
val it = Intent(this, NewMainActivity::class.java)
val pi = PendingIntent.getActivity(this, 0, it, PendingIntent.FLAG_IMMUTABLE)
val notification = NotificationCompat.Builder(this, "layout_inspect_service")
.setContentTitle("布局检查服务已准备就绪")
.setContentText("布局检查服务将在运行一次后退出")
.setContentTitle("SKIP 布局检查服务运行中")
.setSmallIcon(R.drawable.warning)
.setLargeIcon(BitmapFactory.decodeResource(resources, R.drawable.warning))
.setContentIntent(pi)
.build()
startForeground(1, notification)

val intentFilter = IntentFilter(Constants.SKIP_KEY_EVENT_VOLUME_DOWN)
registerReceiver(keyEventVolumeDownReceiver, intentFilter, RECEIVER_NOT_EXPORTED)
}

@RequiresApi(Build.VERSION_CODES.TIRAMISU)
Expand All @@ -99,6 +75,7 @@ class LayoutInspectService: Service() {
override fun onDestroy() {
super.onDestroy()
mMediaProjection?.stop()
virtualDisplay?.release()
DataStoreUtils.putSyncData(SKIP_LAYOUT_INSPECT, false)
}

Expand All @@ -116,58 +93,6 @@ class LayoutInspectService: Service() {
imageReader.surface, null, null
)

imageReader.setOnImageAvailableListener({reader ->
if (!isProcessingImage) {
// 清理缓冲区
reader.acquireLatestImage()?.close()
return@setOnImageAvailableListener
}

val image = reader.acquireLatestImage()
if (image != null) {
val planes = image.planes
val buffer = planes[0].buffer
val pixelStride = planes[0].pixelStride
val rowStride = planes[0].rowStride

// Create Bitmap
val bitmapWithStride = Bitmap.createBitmap(
rowStride / pixelStride,
displayHeight,
Bitmap.Config.ARGB_8888
)
bitmapWithStride.copyPixelsFromBuffer(buffer)
val bitmap = Bitmap.createBitmap(bitmapWithStride, 0, 0, displayWidth, displayHeight)
image.close()

// 保存或处理bitmap
val file = getOutputFile()
val success = saveBitmapToFile(bitmap, file)
if (success) {
ToastManager.showToast(this, "保存成功")
} else {
ToastManager.showToast(this, "保存失败")
}

isProcessingImage = false
}
}, Handler(Looper.getMainLooper()))
}

private fun getOutputFile(): File {
return File(SKIPApp.context.filesDir, "$filename.png")
}

private fun saveBitmapToFile(bitmap: Bitmap, file: File): Boolean {
return try {
val outputStream = FileOutputStream(file)
bitmap.compress(Bitmap.CompressFormat.PNG, 100, outputStream)
outputStream.flush()
outputStream.close()
true
} catch (e: IOException) {
e.printStackTrace()
false
}
LayoutInspectUtils.startScreenCapture(imageReader, displayWidth, displayHeight)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ import android.view.KeyEvent
import android.view.accessibility.AccessibilityEvent
import android.view.accessibility.AccessibilityNodeInfo
import androidx.annotation.RequiresApi
import com.android.skip.SKIPApp
import com.android.skip.SKIP_LAYOUT_INSPECT
import com.android.skip.SKIP_PERMIT_NOTICE
import com.android.skip.handler.BoundsHandler
Expand All @@ -22,29 +21,16 @@ import com.android.skip.handler.TextNodeHandler
import com.android.skip.manager.AnalyticsManager
import com.android.skip.manager.ToastManager
import com.android.skip.manager.WhitelistManager
import com.android.skip.utils.AccessibilityUtils
import com.android.skip.utils.Constants
import com.android.skip.utils.DataStoreUtils
import com.blankj.utilcode.util.LogUtils
import com.blankj.utilcode.util.ScreenUtils
import com.android.skip.utils.LayoutInspectUtils
import com.blankj.utilcode.util.ServiceUtils
import com.blankj.utilcode.util.ZipUtils
import com.google.gson.Gson
import java.io.File

data class MyWindow(val packageName: String, val className: String, val screenHeight: Int, val screenWidth: Int, val nodes: MutableList<MyNodeChild>)

data class MyNode(val node: AccessibilityNodeInfo, val depth: Int, val parentId: Int, val nodeId: Int)

data class MyNodeChild(val depth: Int, val childCount: Int, val parentId: Int, val nodeId: Int, val left: Int, val top: Int, val right: Int, val bottom: Int, var className: String? = null, var text: String?=null, var viewIdResourceName: String?=null)

class MyAccessibilityService : AccessibilityService() {
private val textNodeHandler = TextNodeHandler()
private val idNodeHandler = IdNodeHandler()
private val boundsHandler = BoundsHandler()
private var isLayoutInspect = false
private var layoutInspectClassName: String? = null
private var filename: String? = null

private val foregroundAccessibilityReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action == Constants.FOREGROUND_ACCESSIBILITY_RECEIVER_ACTION) {
Expand All @@ -71,17 +57,7 @@ class MyAccessibilityService : AccessibilityService() {

val rootNode = getCurrentRootNode()

val className = event.className
if (className != null) {
if (!AccessibilityUtils.isSystemClass(className.toString())) {
layoutInspectClassName = className.toString()
}
if (isLayoutInspect) {
isLayoutInspect = false
LogUtils.d("layout inspect className: $layoutInspectClassName")
bfsTraverse(rootNode)
}
}
LayoutInspectUtils.startRecordNodeInfo(rootNode, event.className)

if (!AnalyticsManager.isPerformScan(rootNode.packageName.toString())) return

Expand Down Expand Up @@ -138,69 +114,12 @@ class MyAccessibilityService : AccessibilityService() {
&& event.keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
&& DataStoreUtils.getSyncData(SKIP_LAYOUT_INSPECT, false)
) {
filename = System.currentTimeMillis().toString()

val intent = Intent(Constants.SKIP_KEY_EVENT_VOLUME_DOWN)
intent.putExtra("filename", filename)
intent.setPackage(packageName)
sendBroadcast(intent)

isLayoutInspect = true
LayoutInspectUtils.startLayoutInspect()
return true
}
return super.onKeyEvent(event)
}

private fun bfsTraverse(root: AccessibilityNodeInfo) {
var uniqueId = 0
val queue: MutableList<MyNode> = mutableListOf(MyNode(root, 0, -1, uniqueId))
val temp: MutableList<MyNodeChild> = mutableListOf()

while (queue.isNotEmpty()) {
val (node, depth, parentId, nodeId) = queue.removeAt(0)
processNode(node, temp, depth, parentId, nodeId)

for (i in 0 until node.childCount) {
uniqueId += 1
node.getChild(i)?.let { queue.add(MyNode(it, depth + 1, nodeId, uniqueId)) }
}
}

val window = MyWindow(root.packageName.toString(), layoutInspectClassName.toString(), ScreenUtils.getScreenHeight(), ScreenUtils.getScreenWidth(),temp)
val gson = Gson()
val jsonStr = gson.toJson(window)
val file = File(SKIPApp.context.filesDir, "$filename.json")
file.writeText(jsonStr)

ZipUtils.zipFiles(
listOf(
File(SKIPApp.context.filesDir, "$filename.json"),
File(SKIPApp.context.filesDir, "$filename.png")
),
File(SKIPApp.context.filesDir, "$filename.zip")
)
}

private fun processNode(node: AccessibilityNodeInfo, temp: MutableList<MyNodeChild>, depth: Int, parentId: Int, nodeId: Int) {
val rect = Rect()
node.getBoundsInScreen(rect)
val myNodeChild = MyNodeChild(depth, node.childCount, parentId, nodeId, rect.left, rect.top, rect.right, rect.bottom)

node.className?.let {
myNodeChild.className = it.toString()
}

node.text?.let {
myNodeChild.text = it.toString()
}

node.viewIdResourceName?.let {
myNodeChild.viewIdResourceName = it
}

temp.add(myNodeChild)
}


@RequiresApi(Build.VERSION_CODES.TIRAMISU)
override fun onServiceConnected() {
Expand Down
Loading

0 comments on commit 7e02e24

Please sign in to comment.