(packageName: string)
| 173 | } |
| 174 | |
| 175 | export function inferAndroidAppName(packageName: string): string { |
| 176 | const ignoredTokens = new Set([ |
| 177 | 'com', |
| 178 | 'android', |
| 179 | 'google', |
| 180 | 'app', |
| 181 | 'apps', |
| 182 | 'service', |
| 183 | 'services', |
| 184 | 'mobile', |
| 185 | 'client', |
| 186 | ]); |
| 187 | const tokens = packageName |
| 188 | .split('.') |
| 189 | .flatMap((segment) => segment.split(/[_-]+/)) |
| 190 | .map((token) => token.trim().toLowerCase()) |
| 191 | .filter((token) => token.length > 0); |
| 192 | // Fallback to last token if every token is ignored (e.g. "com.android.app.services" → "Services"). |
| 193 | let chosen = tokens[tokens.length - 1] ?? packageName; |
| 194 | for (let index = tokens.length - 1; index >= 0; index -= 1) { |
| 195 | const token = tokens[index]; |
| 196 | if (token && !ignoredTokens.has(token)) { |
| 197 | chosen = token; |
| 198 | break; |
| 199 | } |
| 200 | } |
| 201 | return chosen |
| 202 | .split(/[^a-z0-9]+/i) |
| 203 | .filter(Boolean) |
| 204 | .map((part) => part.charAt(0).toUpperCase() + part.slice(1)) |
| 205 | .join(' '); |
| 206 | } |
| 207 | |
| 208 | export async function getAndroidAppState(device: DeviceInfo): Promise<AndroidForegroundApp> { |
| 209 | const windowFocus = await readAndroidFocus(device, [ |
no outgoing calls
no test coverage detected