refactor: ###
优化代码并更新文件中的UI元素 - 重构了MainViewModel.kt中的代码,去除了未使用的导入项和不必要的初始化 - 更新了floating_window.xml布局中的样式 - 在AndroidManifest.xml中添加了PACKAGE_USAGE_STATS权限
This commit is contained in:
parent
5dcda8829d
commit
054e86ec13
@ -9,6 +9,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
<uses-permission android:name="android.permission.WAKE_LOCK"/>
|
||||||
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW"/>
|
||||||
|
<uses-permission android:name="android.permission.PACKAGE_USAGE_STATS"/>
|
||||||
|
|
||||||
<application
|
<application
|
||||||
android:allowBackup="true"
|
android:allowBackup="true"
|
||||||
|
@ -6,8 +6,6 @@ import android.content.Intent
|
|||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.provider.Settings
|
import android.provider.Settings
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.View
|
|
||||||
import android.view.WindowManager
|
|
||||||
import androidx.lifecycle.AndroidViewModel
|
import androidx.lifecycle.AndroidViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import kotlinx.coroutines.delay
|
import kotlinx.coroutines.delay
|
||||||
@ -15,7 +13,6 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
|||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.update
|
import kotlinx.coroutines.flow.update
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import java.lang.ref.WeakReference
|
|
||||||
|
|
||||||
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
class MainViewModel(application: Application) : AndroidViewModel(application) {
|
||||||
|
|
||||||
@ -26,9 +23,6 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
|
|
||||||
// region 依赖项
|
// region 依赖项
|
||||||
private val gaidHelper by lazy { GAIDHelper(application) }
|
private val gaidHelper by lazy { GAIDHelper(application) }
|
||||||
private val windowManager by lazy {
|
|
||||||
getApplication<Application>().getSystemService(Context.WINDOW_SERVICE) as WindowManager
|
|
||||||
}
|
|
||||||
// endregion
|
// endregion
|
||||||
|
|
||||||
// region 公共API
|
// region 公共API
|
||||||
@ -58,9 +52,7 @@ class MainViewModel(application: Application) : AndroidViewModel(application) {
|
|||||||
delay(1000) // 延迟 1000 毫秒,确保桌面已经显示
|
delay(1000) // 延迟 1000 毫秒,确保桌面已经显示
|
||||||
try {
|
try {
|
||||||
MyAccessibilityService.getInstance()?.findAndLaunchApp(
|
MyAccessibilityService.getInstance()?.findAndLaunchApp(
|
||||||
"breakingnews",
|
"breakingnews", "io.sixminutes.breakingnews", "Test"
|
||||||
"io.sixminutes.breakingnews",
|
|
||||||
"Test"
|
|
||||||
//"Show Inter Ad 1: 3689d2816239b64e"
|
//"Show Inter Ad 1: 3689d2816239b64e"
|
||||||
)
|
)
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
|
@ -50,7 +50,7 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
private var targetAppName: String? = null
|
private var targetAppName: String? = null
|
||||||
private var targetAppPackageName: String? = null
|
private var targetAppPackageName: String? = null
|
||||||
private var currentAppPackageName: String? = null
|
private var currentAppPackageName: String? = null
|
||||||
private var currentClassName: String? = null
|
private var currentActivityName: String? = null
|
||||||
private var launcherButtonText: String? = null
|
private var launcherButtonText: String? = null
|
||||||
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
|
private val mainHandler by lazy { Handler(Looper.getMainLooper()) }
|
||||||
private var running = false
|
private var running = false
|
||||||
@ -119,13 +119,13 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
// **显示指示器**
|
// **显示指示器**
|
||||||
tapIndicatorView.showTapIndicator(x, y)
|
tapIndicatorView.showTapIndicator(x, y)
|
||||||
|
|
||||||
if (RootHelper.runRootCommand(command)) {
|
// if (RootHelper.runRootCommand(command)) {
|
||||||
Toast.makeText(this, "点击 ($x, $y) 成功", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "点击 ($x, $y) 成功", Toast.LENGTH_SHORT).show()
|
||||||
Log.d(TAG, "Executed: $command")
|
// Log.d(TAG, "Executed: $command")
|
||||||
} else {
|
// } else {
|
||||||
Toast.makeText(this, "点击 ($x, $y) 失败", Toast.LENGTH_SHORT).show()
|
// Toast.makeText(this, "点击 ($x, $y) 失败", Toast.LENGTH_SHORT).show()
|
||||||
Log.e(TAG, "Failed to execute: $command")
|
// Log.e(TAG, "Failed to execute: $command")
|
||||||
}
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -140,6 +140,8 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
buttonText: String? = null,
|
buttonText: String? = null,
|
||||||
): Boolean {
|
): Boolean {
|
||||||
var success = false
|
var success = false
|
||||||
|
// 显示悬浮窗
|
||||||
|
createFloatingWindow()
|
||||||
safeRootOperation { root ->
|
safeRootOperation { root ->
|
||||||
findNodesByCondition(root) { node ->
|
findNodesByCondition(root) { node ->
|
||||||
node.textMatches(appName) || node.contentDescriptionMatches(appName)
|
node.textMatches(appName) || node.contentDescriptionMatches(appName)
|
||||||
@ -150,32 +152,18 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
launcherButtonText = buttonText
|
launcherButtonText = buttonText
|
||||||
targetAppName = appName
|
targetAppName = appName
|
||||||
success = true
|
success = true
|
||||||
|
|
||||||
// 显示悬浮窗
|
|
||||||
createFloatingWindow()
|
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "Failed to click app: $appName")
|
Log.w(TAG, "Failed to click app: $appName")
|
||||||
showToast("Failed to click app: $appName")
|
updateFloatingText("Failed to click app: $appName", true)
|
||||||
}
|
}
|
||||||
} ?: {
|
} ?: {
|
||||||
Log.w(TAG, "App not found: $appName")
|
Log.w(TAG, "App not found: $appName")
|
||||||
showToast("App not found: $appName")
|
updateFloatingText("App not found: $appName", true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return success
|
return success
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isTargetAppRunning(): Boolean {
|
|
||||||
val currentRoot = rootInActiveWindow
|
|
||||||
return if (currentRoot != null) {
|
|
||||||
currentAppPackageName = currentRoot.packageName?.toString()
|
|
||||||
currentAppPackageName == targetAppPackageName
|
|
||||||
} else {
|
|
||||||
Log.e(TAG, "isTargetAppRunning: rootInActiveWindow is null")
|
|
||||||
false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// region 点击逻辑优化
|
// region 点击逻辑优化
|
||||||
/**
|
/**
|
||||||
* 智能点击方案(综合节点属性/父容器/坐标点击)
|
* 智能点击方案(综合节点属性/父容器/坐标点击)
|
||||||
@ -290,6 +278,7 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
retryInterval: Long = DEFAULT_RETRY_INTERVAL
|
retryInterval: Long = DEFAULT_RETRY_INTERVAL
|
||||||
): Boolean {
|
): Boolean {
|
||||||
Log.d(TAG, "Attempting to click button: '$buttonText'")
|
Log.d(TAG, "Attempting to click button: '$buttonText'")
|
||||||
|
updateFloatingText("Attempting to click button: '$buttonText'")
|
||||||
var success = false
|
var success = false
|
||||||
|
|
||||||
safeRootOperation { root ->
|
safeRootOperation { root ->
|
||||||
@ -338,8 +327,7 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
var retryCount = 0
|
var retryCount = 0
|
||||||
|
|
||||||
fun attempt() {
|
fun attempt() {
|
||||||
if (!running)
|
if (!running) return
|
||||||
return
|
|
||||||
when {
|
when {
|
||||||
checkCondition() -> onSuccess()
|
checkCondition() -> onSuccess()
|
||||||
retryCount < maxRetries -> {
|
retryCount < maxRetries -> {
|
||||||
@ -454,75 +442,95 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 检查当前窗口是哪个
|
||||||
|
private fun isTargetAppRunning(): Boolean {
|
||||||
|
val currentRoot = rootInActiveWindow
|
||||||
|
return if (currentRoot != null) {
|
||||||
|
currentAppPackageName = currentRoot.packageName?.toString()
|
||||||
|
currentAppPackageName == targetAppPackageName
|
||||||
|
|
||||||
|
} else {
|
||||||
|
Log.e(TAG, "isTargetAppRunning: rootInActiveWindow is null")
|
||||||
|
updateFloatingText("rootInActiveWindow is null", true)
|
||||||
|
false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun handleWindowChange(event: AccessibilityEvent) {
|
private fun handleWindowChange(event: AccessibilityEvent) {
|
||||||
if (!running) return
|
if (!running) return
|
||||||
|
if (targetAppPackageName == null) return
|
||||||
|
|
||||||
currentAppPackageName = event.packageName?.toString() ?: return
|
val packageName = event.packageName?.toString() ?: "unknownPackageName"
|
||||||
currentClassName = event.className?.toString() ?: return
|
val activity = event.className?.toString() ?: "unknownActivity"
|
||||||
|
|
||||||
|
if (packageName == "io.sixminutes.ridicule" && activity == "android.widget.LinearLayout") {
|
||||||
|
Log.w(TAG, "Detected current app is floating window")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
// 增强日志输出
|
// 增强日志输出
|
||||||
Log.d(
|
Log.d(
|
||||||
TAG, """
|
TAG, """
|
||||||
Window Changed:
|
Window Changed:
|
||||||
├─ Current Package : $currentAppPackageName
|
├─ Current Package : $packageName
|
||||||
├─ Target Package : $targetAppPackageName
|
├─ Target Package : $targetAppPackageName
|
||||||
├─ Class Name : ${currentClassName?.substringAfterLast('.')}
|
├─ Class Name : ${activity.substringAfterLast('.')}
|
||||||
└─ Full Class Name : $currentClassName
|
└─ Full Class Name : $activity
|
||||||
""".trimIndent()
|
""".trimIndent()
|
||||||
)
|
)
|
||||||
if (targetAppPackageName == null) return
|
|
||||||
|
|
||||||
|
currentAppPackageName = packageName
|
||||||
// 当检测到切换到其他应用时
|
// 当检测到切换到其他应用时
|
||||||
if (currentAppPackageName != targetAppPackageName) {
|
if (currentAppPackageName != targetAppPackageName) {
|
||||||
|
checkAppPackageName()
|
||||||
// 先检查 isCheckAppPackageName,如果为 true 则不重复启动
|
|
||||||
if (!isCheckAppPackageName) {
|
|
||||||
isCheckAppPackageName = true
|
|
||||||
mainHandler.postDelayed(::checkAppPackageName, DEFAULT_RETRY_INTERVAL)
|
|
||||||
} else {
|
} else {
|
||||||
Log.w(TAG, "CheckAppPackageName operation is already running, skipping...")
|
currentActivityName = activity
|
||||||
|
checkActivity()
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
// 先检查 isCheckActivity,如果为 true 则不重复启动
|
|
||||||
|
|
||||||
if (!isCheckActivity) {
|
|
||||||
isCheckActivity = true
|
|
||||||
mainHandler.postDelayed(::checkActivity, DEFAULT_RETRY_INTERVAL)
|
|
||||||
} else {
|
|
||||||
Log.w(TAG, "CheckActivity operation is already running, skipping...")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Log.d(
|
|
||||||
TAG,
|
|
||||||
"Current App Package: $currentAppPackageName; Current App Class: $currentClassName; targetAppPackageName: $targetAppPackageName"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查前端应用
|
// 检查前端应用
|
||||||
private fun checkAppPackageName() {
|
private fun checkAppPackageName() {
|
||||||
|
if (isCheckAppPackageName) {
|
||||||
|
Log.w(TAG, "CheckAppPackageName operation is already running, skipping...")
|
||||||
|
updateFloatingText("CheckAppPackageName operation is already running, skipping...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
isCheckAppPackageName = true
|
||||||
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
|
Log.w(TAG, "Detected switch to non-target app: $currentAppPackageName")
|
||||||
try {
|
updateFloatingText("Detected switch to non-target app: $currentAppPackageName")
|
||||||
|
|
||||||
retryOperation(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_INTERVAL, checkCondition = {
|
retryOperation(DEFAULT_MAX_RETRIES, DEFAULT_RETRY_INTERVAL, checkCondition = {
|
||||||
val ok = isTargetAppRunning()
|
val ok = isTargetAppRunning()
|
||||||
ok || performReturnGlobalAction()
|
ok || performReturnGlobalAction()
|
||||||
Log.d(TAG, "Check $targetAppPackageName is activated: $ok")
|
Log.d(TAG, "Check $targetAppPackageName is activated: $ok")
|
||||||
ok
|
ok
|
||||||
}, onSuccess = {
|
}, onSuccess = {
|
||||||
|
isCheckAppPackageName = false // 任务结束后重置
|
||||||
|
updateFloatingText("Successfully returned to target app")
|
||||||
Log.i(TAG, "Successfully returned to target app")
|
Log.i(TAG, "Successfully returned to target app")
|
||||||
|
// 返回后再次检查 Activity
|
||||||
|
mainHandler.postDelayed(::checkActivity, DEFAULT_RETRY_INTERVAL)
|
||||||
}, onFailure = {
|
}, onFailure = {
|
||||||
|
isCheckAppPackageName = false // 任务结束后重置
|
||||||
|
updateFloatingText("Failed to return after $DEFAULT_MAX_RETRIES attempts", true)
|
||||||
Log.e(TAG, "Failed to return after $DEFAULT_MAX_RETRIES attempts")
|
Log.e(TAG, "Failed to return after $DEFAULT_MAX_RETRIES attempts")
|
||||||
returnToHomeAndRestart()
|
returnToHomeAndRestart()
|
||||||
})
|
})
|
||||||
} finally {
|
|
||||||
isCheckAppPackageName = false // 任务结束后重置
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 检查当前 Activity
|
// 检查当前 Activity
|
||||||
private fun checkActivity() {
|
private fun checkActivity() {
|
||||||
Log.w(TAG, "Current class name is : $currentClassName")
|
if (isCheckActivity) {
|
||||||
|
Log.w(TAG, "CheckActivity operation is already running, skipping...")
|
||||||
|
updateFloatingText("CheckActivity operation is already running, skipping...")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
isCheckActivity = true
|
||||||
|
Log.w(TAG, "Current class name is : $currentActivityName")
|
||||||
try {
|
try {
|
||||||
when (currentClassName) {
|
when (currentActivityName) {
|
||||||
"io.sixminutes.breakingnews.MainActivity" -> launcherButtonText?.let {
|
"io.sixminutes.breakingnews.MainActivity" -> launcherButtonText?.let {
|
||||||
findAndClickButton(it)
|
findAndClickButton(it)
|
||||||
}
|
}
|
||||||
@ -538,6 +546,13 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
"com.applovin.adview.AppLovinFullscreenActivity" -> simulateTap(648, 62)
|
"com.applovin.adview.AppLovinFullscreenActivity" -> simulateTap(648, 62)
|
||||||
|
else -> {
|
||||||
|
Log.d(TAG, "current Activity is unknown")
|
||||||
|
updateFloatingText("current Activity is unknown")
|
||||||
|
mainHandler.postDelayed({
|
||||||
|
checkActivity()
|
||||||
|
}, DEFAULT_RETRY_INTERVAL)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
isCheckActivity = false
|
isCheckActivity = false
|
||||||
@ -564,8 +579,9 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
*/
|
*/
|
||||||
private fun returnToHomeAndRestart(): Boolean {
|
private fun returnToHomeAndRestart(): Boolean {
|
||||||
Log.d(TAG, "Used home+restart strategy")
|
Log.d(TAG, "Used home+restart strategy")
|
||||||
|
updateFloatingText("used home+restart strategy")
|
||||||
return performGlobalAction(GLOBAL_ACTION_HOME) && targetAppName != null && targetAppPackageName != null && findAndLaunchApp(
|
return performGlobalAction(GLOBAL_ACTION_HOME) && targetAppName != null && targetAppPackageName != null && findAndLaunchApp(
|
||||||
targetAppName!!, targetAppPackageName!!
|
targetAppName!!, targetAppPackageName!!, launcherButtonText
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -602,7 +618,7 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
// 设置悬浮窗口显示在屏幕的左上角,x,y 为偏移量
|
// 设置悬浮窗口显示在屏幕的左上角,x,y 为偏移量
|
||||||
layoutParams.gravity = android.view.Gravity.BOTTOM or android.view.Gravity.END
|
layoutParams.gravity = android.view.Gravity.BOTTOM or android.view.Gravity.END
|
||||||
layoutParams.x = 0
|
layoutParams.x = 0
|
||||||
layoutParams.y = 100
|
layoutParams.y = 150
|
||||||
|
|
||||||
// 将悬浮窗口添加到 WindowManager 中显示
|
// 将悬浮窗口添加到 WindowManager 中显示
|
||||||
windowManager?.addView(floatingView, layoutParams)
|
windowManager?.addView(floatingView, layoutParams)
|
||||||
@ -636,10 +652,17 @@ class MyAccessibilityService : AccessibilityService() {
|
|||||||
* 更新悬浮窗口中显示的文本
|
* 更新悬浮窗口中显示的文本
|
||||||
* @param text 要显示的文本内容
|
* @param text 要显示的文本内容
|
||||||
*/
|
*/
|
||||||
private fun updateFloatingText(text: String) {
|
private fun updateFloatingText(text: String, isError: Boolean = false) {
|
||||||
// 在悬浮窗口中找到 TextView 并设置新文本
|
// 在悬浮窗口中找到 TextView 并设置新文本
|
||||||
val textView = floatingView?.findViewById<TextView>(R.id.first_message)
|
if (isError) {
|
||||||
textView?.text = text
|
val errorView = floatingView?.findViewById<TextView>(R.id.error_message)
|
||||||
|
errorView?.text = text
|
||||||
|
} else {
|
||||||
|
val info1 = floatingView?.findViewById<TextView>(R.id.info_message_1)
|
||||||
|
val info2 = floatingView?.findViewById<TextView>(R.id.info_message_2)
|
||||||
|
info2?.text = info1?.text
|
||||||
|
info1?.text = text
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1,49 +1,36 @@
|
|||||||
<!-- res/layout/floating_window.xml -->
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
<LinearLayout
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools"
|
|
||||||
android:id="@+id/frameLayout2"
|
android:id="@+id/frameLayout2"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:layout_gravity="bottom|end"
|
android:background="#22000080"
|
||||||
android:background="#22000000">
|
android:padding="8dp"
|
||||||
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/first_message"
|
android:id="@+id/info_message_2"
|
||||||
android:layout_width="240ddp"
|
android:layout_width="240dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:text="2"
|
android:text=""
|
||||||
android:textSize="18sp"
|
android:textSize="12sp"/>
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/first_second"
|
android:id="@+id/info_message_1"
|
||||||
android:layout_width="240pdp"
|
android:layout_width="240dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:text="1"
|
android:text=""
|
||||||
android:textSize="18sp"
|
android:textSize="12sp"/>
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintHorizontal_bias="0.0"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent"
|
|
||||||
app:layout_constraintVertical_bias="1.0" />
|
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/main_message"
|
android:id="@+id/error_message"
|
||||||
android:layout_width="240pdp"
|
android:layout_width="240dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:padding="2dp"
|
android:padding="2dp"
|
||||||
android:text="xxxx"
|
android:text=""
|
||||||
android:textSize="18sp"
|
android:textColor="#ff0000"
|
||||||
app:layout_constraintBottom_toTopOf="@+id/first_message"
|
android:textSize="14sp"/>
|
||||||
app:layout_constraintEnd_toEndOf="parent"
|
|
||||||
app:layout_constraintStart_toStartOf="parent"
|
|
||||||
app:layout_constraintTop_toTopOf="parent" />
|
|
||||||
|
|
||||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
</LinearLayout>
|
Loading…
x
Reference in New Issue
Block a user