or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

Files

docs

app-management.mddevice-management.mdimage-processing.mdindex.mdscreen-input.mdui-interaction.mdwatchers-automation.mdxpath-selection.md

watchers-automation.mddocs/

0

# Watchers and Automation

1

2

Automated response system for handling popups, dialogs, and recurring UI events through configurable watchers and monitoring contexts.

3

4

## Capabilities

5

6

### Watcher System

7

8

Monitor UI for specific elements and execute automated responses.

9

10

```python { .api }

11

class Device:

12

@cached_property

13

def watcher(self) -> Watcher:

14

"""Access device watcher for automated UI responses"""

15

16

class Watcher:

17

def when(self, selector: Dict[str, Any]) -> WatcherItem:

18

"""

19

Create watcher for UI element.

20

21

Parameters:

22

- selector: UI element selector criteria

23

24

Returns:

25

WatcherItem for configuring response

26

"""

27

28

def remove(self, name: str = None):

29

"""

30

Remove watcher by name.

31

32

Parameters:

33

- name: Watcher name to remove, None removes all

34

"""

35

36

def list(self) -> List[str]:

37

"""List all active watcher names"""

38

39

def run(self):

40

"""Run watchers once to check for triggers"""

41

42

def start(self, interval: float = 2.0):

43

"""

44

Start background watcher monitoring.

45

46

Parameters:

47

- interval: Check interval in seconds

48

"""

49

50

def stop(self):

51

"""Stop background watcher monitoring"""

52

```

53

54

Usage examples:

55

56

```python

57

d = u2.connect()

58

59

# Create watchers for common dialogs

60

d.watcher.when(text="OK").click()

61

d.watcher.when(text="Allow").click()

62

d.watcher.when(text="Cancel").click()

63

64

# Handle permission dialogs

65

d.watcher.when(textContains="permission").click(text="Allow")

66

d.watcher.when(textContains="Location").click(text="Grant")

67

68

# Start background monitoring

69

d.watcher.start(interval=1.0) # Check every second

70

71

# Your automation code here

72

d.app_start("com.example.app")

73

# Watchers automatically handle popups

74

75

# Stop monitoring when done

76

d.watcher.stop()

77

78

# Remove specific watchers

79

d.watcher.remove("OK_watcher")

80

d.watcher.remove() # Remove all

81

```

82

83

### Watch Context

84

85

Advanced watching system with XPath support and context management.

86

87

```python { .api }

88

class Device:

89

def watch_context(self, autostart: bool = True, builtin: bool = False) -> WatchContext:

90

"""

91

Create watch context for advanced automation.

92

93

Parameters:

94

- autostart: Start monitoring automatically

95

- builtin: Use built-in system watchers

96

97

Returns:

98

WatchContext for configuring watchers

99

"""

100

101

class WatchContext:

102

def __init__(self, d: Device, builtin: bool = False):

103

"""Initialize watch context"""

104

105

def when(self, xpath: str) -> WatchContextItem:

106

"""

107

Create XPath-based watcher.

108

109

Parameters:

110

- xpath: XPath expression to watch for

111

112

Returns:

113

WatchContextItem for configuring response

114

"""

115

116

def start(self):

117

"""Start watch context monitoring"""

118

119

def stop(self):

120

"""Stop watch context monitoring"""

121

122

def wait_stable(self, timeout: float = 10.0) -> bool:

123

"""

124

Wait for UI to become stable (no watcher triggers).

125

126

Parameters:

127

- timeout: Maximum wait time

128

129

Returns:

130

bool: True if UI became stable

131

"""

132

```

133

134

Usage examples:

135

136

```python

137

d = u2.connect()

138

139

# Create watch context

140

with d.watch_context() as ctx:

141

# XPath-based watchers

142

ctx.when('//*[@text="OK"]').click()

143

ctx.when('//*[@text="Skip"]').click()

144

ctx.when('//android.widget.Button[contains(@text, "Allow")]').click()

145

146

# Handle ads and interruptions

147

ctx.when('//*[@resource-id="close_ad"]').click()

148

ctx.when('//*[contains(@text, "Ad")]/..//*[@text="✕"]').click()

149

150

# Run automation

151

d.app_start("com.example.app")

152

153

# Wait for UI to stabilize

154

ctx.wait_stable(timeout=30)

155

156

# Continue with automation

157

d.click(100, 200)

158

159

# Context automatically stops when exiting

160

```

161

162

### Advanced Watcher Configuration

163

164

Configure complex watcher behaviors and responses.

165

166

```python { .api }

167

class WatcherItem:

168

def click(self, **kwargs) -> WatcherItem:

169

"""Click matching element with optional selector refinement"""

170

171

def press(self, key: str) -> WatcherItem:

172

"""Press hardware key when element appears"""

173

174

def call(self, func) -> WatcherItem:

175

"""Call custom function when element appears"""

176

177

class WatchContextItem:

178

def click(self) -> WatchContextItem:

179

"""Click the matched XPath element"""

180

181

def press(self, key: str) -> WatchContextItem:

182

"""Press hardware key when XPath matches"""

183

184

def call(self, func) -> WatchContextItem:

185

"""Call custom function when XPath matches"""

186

```

187

188

Usage examples:

189

190

```python

191

d = u2.connect()

192

193

# Custom watcher functions

194

def handle_popup():

195

print("Popup appeared, handling...")

196

d.press("back")

197

d.press("back")

198

199

def skip_ad():

200

print("Ad detected, skipping...")

201

d.click(0.9, 0.1) # Top-right corner

202

203

# Configure watcher responses

204

d.watcher.when(text="Update").press("back")

205

d.watcher.when(textContains="popup").call(handle_popup)

206

d.watcher.when(resourceId="ad_container").call(skip_ad)

207

208

# XPath watcher with custom function

209

with d.watch_context() as ctx:

210

ctx.when('//*[@text="Rate App"]').call(lambda: d.press("back"))

211

ctx.when('//android.widget.VideoView').call(skip_ad)

212

213

# Run automation with watchers active

214

d.app_start("com.social.app")

215

ctx.wait_stable()

216

```

217

218

### Built-in System Watchers

219

220

Use pre-configured watchers for common system dialogs.

221

222

```python

223

d = u2.connect()

224

225

# Enable built-in system watchers

226

with d.watch_context(builtin=True) as ctx:

227

# Built-in watchers handle:

228

# - ANR (Application Not Responding) dialogs

229

# - Permission dialogs

230

# - System update notifications

231

# - Crash dialogs

232

233

# Your automation code

234

d.app_start("com.potentially.unstable.app")

235

236

# Built-in watchers automatically handle system interruptions

237

ctx.wait_stable(timeout=60)

238

```

239

240

### Watcher Monitoring and Debugging

241

242

Monitor watcher activity and debug watcher configurations.

243

244

```python

245

d = u2.connect()

246

247

# List active watchers

248

watchers = d.watcher.list()

249

print(f"Active watchers: {watchers}")

250

251

# Manual watcher execution

252

d.watcher.run() # Check all watchers once

253

254

# Monitor watcher triggers

255

import logging

256

u2.enable_pretty_logging(logging.DEBUG)

257

258

# Create watchers with names for tracking

259

d.watcher.when(text="OK").click().name("ok_handler")

260

d.watcher.when(text="Cancel").press("back").name("cancel_handler")

261

262

# Start monitoring with logging

263

d.watcher.start(interval=0.5)

264

265

# Run automation - watcher activity will be logged

266

d.app_start("com.example.app")

267

time.sleep(10)

268

269

d.watcher.stop()

270

```

271

272

### Complex Automation Patterns

273

274

Combine watchers with application lifecycle for robust automation.

275

276

```python

277

d = u2.connect()

278

279

def robust_app_automation(package_name):

280

"""Robust app automation with comprehensive error handling"""

281

282

with d.watch_context(builtin=True) as ctx:

283

# Handle common interruptions

284

ctx.when('//*[@text="OK"]').click()

285

ctx.when('//*[@text="Allow"]').click()

286

ctx.when('//*[@text="Skip"]').click()

287

ctx.when('//*[@text="Not now"]').click()

288

ctx.when('//*[contains(@text, "Update")]').click(text="Later")

289

290

# Handle ads

291

ctx.when('//*[@resource-id="close_button"]').click()

292

ctx.when('//*[@text="✕"]').click()

293

294

# Launch app with session monitoring

295

with d.session(package_name) as session:

296

ctx.wait_stable(timeout=30)

297

298

# App-specific automation

299

yield session # Allow caller to perform actions

300

301

# Verify app is still running

302

if not session.running():

303

print("App crashed, restarting...")

304

session.restart()

305

ctx.wait_stable(timeout=15)

306

307

# Usage

308

for session in robust_app_automation("com.example.app"):

309

# Perform automation with robust error handling

310

session.click(100, 200)

311

session.send_keys("test input")

312

313

# UI interactions protected by watchers

314

session(text="Submit").click()

315

316

# Wait and continue

317

time.sleep(5)

318

```

319

320

### Toast Monitoring Integration

321

322

Combine watchers with toast message monitoring for comprehensive UI monitoring.

323

324

```python

325

d = u2.connect()

326

327

# Monitor toasts alongside watchers

328

with d.watch_context() as ctx:

329

# Handle error dialogs

330

ctx.when('//*[@text="Error"]').click(text="OK")

331

ctx.when('//*[@text="Failed"]').press("back")

332

333

# Start app

334

d.app_start("com.example.app")

335

336

# Monitor for success/error messages

337

ctx.wait_stable()

338

339

# Check toast messages

340

toast = d.last_toast

341

if toast and "error" in toast.lower():

342

print(f"Error toast detected: {toast}")

343

d.press("back")

344

elif toast and "success" in toast.lower():

345

print(f"Success toast: {toast}")

346

347

d.clear_toast()

348

```