如何实现从应用外EditText字段全选并执行复制/剪切操作?
Hey there! Let's break down how to solve your two related problems step by step—first accessing and copying text from external EditTexts, then triggering that action with a phone shake.
First off, let's get one thing straight: on Android, you can't directly access UI elements from other apps due to the system's sandbox security. The only legitimate way to do this is using an Accessibility Service—this is the official tool for interacting with other apps' UI in a controlled manner.
Step 1: Create an Accessibility Service Subclass
This service will handle finding focused EditTexts and performing select/copy/cut actions. Here's a Kotlin implementation:
class ShakeCopyAccessibilityService : AccessibilityService() { override fun onAccessibilityEvent(event: AccessibilityEvent) { // We'll handle shake-triggered actions separately, but this can listen to focus events too } override fun onInterrupt() { // Cleanup logic if the service is interrupted } // Method to trigger select-all + copy/cut on focused EditText fun performTextAction(action: Int) { val rootNode = rootInActiveWindow ?: return val focusedEditText = findFocusedEditText(rootNode) focusedEditText?.let { // First select all text in the EditText it.performAction(AccessibilityNodeInfo.ACTION_SELECT_ALL) // Then execute copy or cut action it.performAction(action) // Recycle the node to avoid memory leaks it.recycle() } } // Recursively search the UI tree for the focused EditText private fun findFocusedEditText(node: AccessibilityNodeInfo): AccessibilityNodeInfo? { if (node.isFocused && node.className == EditText::class.java.name) { return node } for (i in 0 until node.childCount) { val childNode = node.getChild(i) ?: continue val result = findFocusedEditText(childNode) if (result != null) { return result } childNode.recycle() } return null } }
Step 2: Register the Service in AndroidManifest
Add this to your AndroidManifest.xml—note the required system permission and configuration reference:
<service android:name=".ShakeCopyAccessibilityService" android:permission="android.permission.BIND_ACCESSIBILITY_SERVICE"> <intent-filter> <action android:name="android.accessibilityservice.AccessibilityService" /> </intent-filter> <meta-data android:name="android.accessibilityservice" android:resource="@xml/accessibility_service_config" /> </service>
Step 3: Create the Accessibility Service Config
Make a new file res/xml/accessibility_service_config.xml to define the service's capabilities:
<?xml version="1.0" encoding="utf-8"?> <accessibility-service xmlns:android="http://schemas.android.com/apk/res/android" android:accessibilityEventTypes="typeViewFocused" android:accessibilityFeedbackType="feedbackGeneric" android:accessibilityFlags="flagDefault" android:canPerformGestures="true" android:description="@string/accessibility_service_desc" />
Add the service description to your strings.xml (users will see this in accessibility settings):
<string name="accessibility_service_desc">Allows this app to select and copy/cut text from other apps\' input fields when you shake your phone.</string>
To detect a shake, we'll use the device's accelerometer via SensorManager, then trigger the accessibility service's text action when a shake is detected.
Step 1: Listen for Shake Events in Your Activity
Add this to your main activity (use a foreground service if you want the feature to work in the background):
class MainActivity : AppCompatActivity() { private lateinit var sensorManager: SensorManager private var accelerometer: Sensor? = null private var lastAccel = 0f private val shakeThreshold = 18f // Adjust this based on testing (lower = more sensitive) // Reference to our accessibility service private var accessibilityService: ShakeCopyAccessibilityService? = null // Bind to the service to call its methods private val serviceConnection = object : ServiceConnection { override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val binder = service as ShakeCopyAccessibilityService.LocalBinder accessibilityService = binder.getService() } override fun onServiceDisconnected(name: ComponentName?) { accessibilityService = null } } private val sensorListener = object : SensorEventListener { override fun onSensorChanged(event: SensorEvent) { val x = event.values[0] val y = event.values[1] val z = event.values[2] val currentAccel = sqrt((x*x + y*y + z*z).toDouble()).toFloat() val deltaAccel = currentAccel - lastAccel lastAccel = currentAccel // Trigger action when a shake is detected if (deltaAccel > shakeThreshold) { accessibilityService?.let { // Use AccessibilityNodeInfo.ACTION_CUT for cut instead of copy it.performTextAction(AccessibilityNodeInfo.ACTION_COPY) } } } override fun onAccuracyChanged(sensor: Sensor?, accuracy: Int) {} } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) // Initialize sensor manager sensorManager = getSystemService(Context.SENSOR_SERVICE) as SensorManager accelerometer = sensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER) // Bind to the accessibility service Intent(this, ShakeCopyAccessibilityService::class.java).also { intent -> bindService(intent, serviceConnection, Context.BIND_AUTO_CREATE) } // Prompt user to enable accessibility service if it's not active if (!isAccessibilityServiceEnabled()) { startActivity(Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS)) } } // Helper to check if our service is enabled in accessibility settings private fun isAccessibilityServiceEnabled(): Boolean { val enabledServices = Settings.Secure.getString( contentResolver, Settings.Secure.ENABLED_ACCESSIBILITY_SERVICES ) val packageName = packageName return enabledServices?.contains("$packageName/.ShakeCopyAccessibilityService") == true } override fun onResume() { super.onResume() accelerometer?.let { sensorManager.registerListener(sensorListener, it, SensorManager.SENSOR_DELAY_NORMAL) } } override fun onPause() { super.onPause() sensorManager.unregisterListener(sensorListener) } override fun onDestroy() { super.onDestroy() unbindService(serviceConnection) } }
Step 2: Add Local Binder to Accessibility Service
To let the activity call the service's methods, add a binder inside ShakeCopyAccessibilityService:
inner class LocalBinder : Binder() { fun getService(): ShakeCopyAccessibilityService = this@ShakeCopyAccessibilityService } override fun onBind(intent: Intent): IBinder { return LocalBinder() }
- User Consent is Mandatory: Android requires users to manually enable accessibility services in Settings. Always guide them to do this on first launch.
- Tune the Shake Threshold: Adjust
shakeThresholdto avoid false triggers—normal phone movement shouldn't activate the feature. - Memory Management: Always call
recycle()onAccessibilityNodeInfoobjects to prevent memory leaks. - Compatibility Limits: Some apps (like banking or secure apps) block accessibility access for security reasons—your feature won't work in these cases.
内容的提问来源于stack exchange,提问作者Loktakpat Tonda

