Skip to main content
Glama
ClearText.ts3.74 kB
import { AdbUtils } from "../../utils/android-cmdline-tools/adb"; import { BaseVisualChange, ProgressCallback } from "./BaseVisualChange"; import { BootedDevice, ClearTextResult } from "../../models"; import { ElementUtils } from "../utility/ElementUtils"; import { ObserveResult } from "../../models"; import { Axe } from "../../utils/ios-cmdline-tools/axe"; export class ClearText extends BaseVisualChange { private elementUtils: ElementUtils; constructor(device: BootedDevice, adb: AdbUtils | null = null, axe: Axe | null = null) { super(device, adb, axe); this.elementUtils = new ElementUtils(); } async execute(progress?: ProgressCallback): Promise<ClearTextResult> { return this.observedInteraction( async (observeResult: ObserveResult) => { try { if (!observeResult.viewHierarchy) { // Fallback: if we can't get view hierarchy, use a reasonable default await this.clearWithDeletes(200); return { success: true }; } let textLength = 0; // Look for focused elements first by traversing and checking attributes textLength = this.findFocusedElementTextLength(observeResult.viewHierarchy); // TODO: Move cursor to the end of the text if (textLength > 0) { await this.clearWithDeletes(textLength); } return { success: true }; } catch (error) { return { success: false, error: "Failed to clear text" }; } }, { changeExpected: false, // TODO: can only make this true once we know for sure there was text in the text field tolerancePercent: 0.00, timeoutMs: 100, progress } ); } private findFocusedElementTextLength(viewHierarchy: any): number { let textLength = 0; const rootNodes = this.elementUtils.extractRootNodes(viewHierarchy); for (const rootNode of rootNodes) { this.elementUtils.traverseNode(rootNode, (node: any) => { const nodeProperties = this.elementUtils.extractNodeProperties(node); if ((nodeProperties.focused === "true" || nodeProperties.focused === true) && nodeProperties.text && typeof nodeProperties.text === "string") { textLength = Math.max(textLength, nodeProperties.text.length); } }); } return textLength; } private findAnyTextInputLength(viewHierarchy: any): number { let textLength = 0; const rootNodes = this.elementUtils.extractRootNodes(viewHierarchy); // Common input field classes const inputClasses = [ "android.widget.EditText", "android.widget.AutoCompleteTextView", "android.widget.MultiAutoCompleteTextView", "androidx.appcompat.widget.AppCompatEditText" ]; for (const rootNode of rootNodes) { this.elementUtils.traverseNode(rootNode, (node: any) => { const nodeProperties = this.elementUtils.extractNodeProperties(node); if (nodeProperties.class && inputClasses.some(cls => nodeProperties.class.includes(cls)) && nodeProperties.text && typeof nodeProperties.text === "string") { textLength = Math.max(textLength, nodeProperties.text.length); } }); } return textLength; } private async clearWithDeletes(count: number): Promise<void> { // Move to end of field first to ensure we're deleting from the right position await this.adb.executeCommand("shell input keyevent KEYCODE_MOVE_END"); // Send KEYCODE_DEL commands with no delay for (let i = 0; i < count; i++) { await this.adb.executeCommand("shell input keyevent KEYCODE_DEL"); } } }

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/zillow/auto-mobile'

If you have feedback or need assistance with the MCP directory API, please join our Discord server