Compare commits
11 Commits
5fbbe3d273
...
fix/e2e-de
| Author | SHA1 | Date | |
|---|---|---|---|
| 4b645c6fb2 | |||
| c7c7ce29f8 | |||
| b36dde77d8 | |||
| 93b2140844 | |||
| 67c3d464e0 | |||
| d298f03891 | |||
| 604f2d7341 | |||
| cd8f35056c | |||
| 800fb1da98 | |||
| 6cd068f951 | |||
| f46b0b7bcb |
@@ -29,6 +29,13 @@ Found in `sensors/honeypot_radome.py`.
|
||||
- **Ghost Engagement Guard**: Strips DOM nodes explicitly tagged with `visible-to-user="false"` to prevent triggering Accessibility Hooks.
|
||||
- **VLM Sanity Guard**: Woven into `telepathic_engine.py`, it sends semantic matches for destructive actions (Like/Follow) through a Vision Language Model step to prevent executing semantic "Bait and Switch" tricks.
|
||||
|
||||
### 🧠 Situational Awareness Engine (SAE)
|
||||
Found in `situational_awareness.py`. Handles autonomous obstacle detection, recovery, and learning without hardcoded rules.
|
||||
- **3-Layer Modal Fast-Path**: Eliminates LLM hallucination traps for Instagram-internal modals (surveys, rating prompts) via O(1) deterministic structural checks:
|
||||
1. **Resource-ID Guard**: Detects internal blocking overlays (e.g., `survey_overlay_container`, `nux_overlay`).
|
||||
2. **Dismiss-Button Heuristic**: Cross-validates typical negative actions ("Not Now", "Take Survey") with overlay structures to prevent false positives in post captions.
|
||||
3. **Zero-Deception Fallback**: If structural markers fail, falls back to `ScreenMemoryDB` and ultimately the LLM. Structured invariants always override the semantic cache.
|
||||
|
||||
### 🦾 Biometric Facade (Gaussian Clicks)
|
||||
Found in `device_facade.py`.
|
||||
- Human touches do not follow a flat mathematical uniform grid. The GramPilot simulates genuine **biometric dispersion** using `random.gauss(mu, sigma)`, strictly centering clicks inside a thumb-bias radius (bottom-left skew for right-handers). In tests, this hits a 68% standard deviation precision.
|
||||
|
||||
@@ -29,9 +29,11 @@ class PerfectSnappingPlugin(BehaviorPlugin):
|
||||
if not getattr(self, "_enabled", True):
|
||||
return False
|
||||
|
||||
xml_lower = ctx.context_xml.lower()
|
||||
# Do not snap if we are on a profile page or grid, it's meant for posts.
|
||||
if "profile_tabs_container" in xml_lower or "explore_grid" in xml_lower:
|
||||
# Perfect snapping is only for feed posts.
|
||||
# Do not snap if we are on a profile page, explore grid, or modal.
|
||||
from GramAddict.core.perception.feed_analysis import has_feed_markers
|
||||
|
||||
if not has_feed_markers(ctx.context_xml):
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import logging
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
|
||||
try:
|
||||
import psutil
|
||||
@@ -557,7 +558,9 @@ def start_bot(**kwargs):
|
||||
continue
|
||||
elif current_target == "StoriesFeed":
|
||||
logger.info("📱 Locating story tray on HomeFeed...")
|
||||
nav_graph.do("tap story ring avatar")
|
||||
if not nav_graph.do("tap story ring avatar"):
|
||||
logger.warning("❌ Failed to tap story ring avatar. Retrying next loop.")
|
||||
continue
|
||||
post_loaded = _wait_for_story_loaded(device, timeout=5)
|
||||
if not post_loaded:
|
||||
logger.warning("❌ Stories failed to open from HomeFeed. Retrying next loop.")
|
||||
@@ -682,6 +685,7 @@ def _interact_with_profile(device, configs, username, session_state, sleep_mod,
|
||||
|
||||
if cognitive_stack is None:
|
||||
cognitive_stack = {}
|
||||
_validate_cognitive_stack(cognitive_stack, "ProfileInteraction")
|
||||
|
||||
if hasattr(session_state, "my_username") and username == session_state.my_username:
|
||||
logger.info(f"🤝 [Deep Interaction] Skipping own profile @{username} to prevent self-interactions.")
|
||||
@@ -821,6 +825,7 @@ def _run_zero_latency_stories_loop(device, configs, session_state, cognitive_sta
|
||||
|
||||
from colorama import Fore
|
||||
|
||||
_validate_cognitive_stack(cognitive_stack, "StoriesLoop")
|
||||
logger.info("🎬 [StoriesFeed] Starting native story binging loop...", extra={"color": f"{Fore.CYAN}"})
|
||||
|
||||
dopamine = cognitive_stack.get("dopamine")
|
||||
@@ -855,6 +860,20 @@ def _run_zero_latency_stories_loop(device, configs, session_state, cognitive_sta
|
||||
logger.warning("Failed to dump UI hierarchy in StoriesFeed.")
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
# ── Perimeter Guard: Verify we're still inside Instagram ──
|
||||
# Production bug 2026-05-03: A story's swipe-up link opened the Play Store,
|
||||
# and the loop kept tapping blindly on com.android.vending for 5+ iterations.
|
||||
packages = set(re.findall(r'package="([^"]+)"', xml_dump))
|
||||
app_id = getattr(device, "app_id", "com.instagram.android")
|
||||
if packages and app_id not in packages:
|
||||
logger.error(
|
||||
f"🚨 [StoriesFeed] FOREIGN APP DETECTED! Packages: {packages}. "
|
||||
f"A story link likely opened an external app. Aborting loop."
|
||||
)
|
||||
device.press("back")
|
||||
sleep(1.5)
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
if getattr(configs.args, "ignore_close_friends", False):
|
||||
if "enge freunde" in xml_dump.lower() or "close friend" in xml_dump.lower():
|
||||
logger.info(
|
||||
@@ -876,6 +895,36 @@ def _run_zero_latency_stories_loop(device, configs, session_state, cognitive_sta
|
||||
return "FEED_EXHAUSTED"
|
||||
|
||||
|
||||
def _validate_cognitive_stack(cognitive_stack, context_name):
|
||||
"""
|
||||
Validates that the cognitive stack has all required engine dependencies
|
||||
injected properly. This is the ultimate zero-trust guard for plugins.
|
||||
"""
|
||||
if not isinstance(cognitive_stack, dict):
|
||||
raise TypeError(f"[{context_name}] CognitiveStack must be a dict, got {type(cognitive_stack)}")
|
||||
|
||||
required_engines = [
|
||||
"dopamine",
|
||||
"darwin",
|
||||
"resonance",
|
||||
"active_inference",
|
||||
"growth_brain",
|
||||
"swarm",
|
||||
"writer",
|
||||
"nav_graph",
|
||||
"zero_engine",
|
||||
"telepathic",
|
||||
]
|
||||
|
||||
missing = [eng for eng in required_engines if eng not in cognitive_stack or cognitive_stack[eng] is None]
|
||||
if missing:
|
||||
import logging
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
logger.error(f"🚨 [{context_name}] CognitiveStack missing required engines: {missing}")
|
||||
raise ValueError(f"[{context_name}] CognitiveStack missing required engines: {missing}")
|
||||
|
||||
|
||||
def _run_zero_latency_feed_loop(
|
||||
device, zero_engine, nav_graph, configs, session_state, job_target, cognitive_stack, is_reels=False
|
||||
):
|
||||
@@ -889,6 +938,7 @@ def _run_zero_latency_feed_loop(
|
||||
- Darwin is the SOLE dwell controller → no duplicate sleep calls
|
||||
- SwarmProtocol emits pheromones after successful interactions
|
||||
"""
|
||||
_validate_cognitive_stack(cognitive_stack, "FeedLoop")
|
||||
logger.info(f"🔄 Entering Zero-Latency Interaction Pool. Feed: {job_target}")
|
||||
|
||||
dopamine = cognitive_stack.get("dopamine")
|
||||
@@ -927,7 +977,9 @@ def _run_zero_latency_feed_loop(
|
||||
|
||||
# 🛡️ Structural Guard: Curiosity targets (DMs, Notifications) are ONLY available on HomeFeed.
|
||||
# We must navigate there first, breaking current context.
|
||||
nav_graph.navigate_to("HomeFeed", zero_engine)
|
||||
if not nav_graph.navigate_to("HomeFeed", zero_engine):
|
||||
logger.warning("❌ [Curiosity] Failed to navigate to HomeFeed. Aborting curiosity check.")
|
||||
continue
|
||||
sleep(random.uniform(1.0, 2.5))
|
||||
|
||||
dm_config = configs.get_plugin_config("dm_reply")
|
||||
@@ -966,6 +1018,17 @@ def _run_zero_latency_feed_loop(
|
||||
if cognitive_stack.get("radome"):
|
||||
context_xml = cognitive_stack.get("radome").sanitize_xml(context_xml)
|
||||
|
||||
# ── Perimeter Guard: Verify we're still inside Instagram ──
|
||||
# Parity with story loop guard (production bug 2026-05-03).
|
||||
if context_xml:
|
||||
feed_packages = set(re.findall(r'package="([^"]+)"', context_xml))
|
||||
feed_app_id = getattr(device, "app_id", "com.instagram.android")
|
||||
if feed_packages and feed_app_id not in feed_packages:
|
||||
logger.error(f"🚨 [FeedLoop] FOREIGN APP DETECTED! Packages: {feed_packages}. Aborting loop.")
|
||||
device.press("back")
|
||||
sleep(1.5)
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
# ── Execute Plugin Registry Behaviors (Feed Level) ──
|
||||
from GramAddict.core.behaviors import BehaviorContext, PluginRegistry
|
||||
|
||||
@@ -1025,6 +1088,7 @@ def _run_zero_latency_search_loop(
|
||||
"""
|
||||
Executes the autonomous Search & Interact logic.
|
||||
"""
|
||||
_validate_cognitive_stack(cognitive_stack, "SearchLoop")
|
||||
logger.info("🧠 [Search Engine] Initiating keyword discovery...", extra={"color": f"{Style.BRIGHT}{Fore.CYAN}"})
|
||||
|
||||
import random
|
||||
@@ -1050,6 +1114,16 @@ def _run_zero_latency_search_loop(
|
||||
xml = device.dump_hierarchy()
|
||||
telepathic = cognitive_stack.get("telepathic")
|
||||
|
||||
# ── Perimeter Guard: Verify we're still inside Instagram ──
|
||||
if xml:
|
||||
search_packages = set(re.findall(r'package="([^"]+)"', xml))
|
||||
search_app_id = getattr(device, "app_id", "com.instagram.android")
|
||||
if search_packages and search_app_id not in search_packages:
|
||||
logger.error(f"🚨 [SearchLoop] FOREIGN APP DETECTED! Packages: {search_packages}. Aborting loop.")
|
||||
device.press("back")
|
||||
sleep(1.5)
|
||||
return "CONTEXT_LOST"
|
||||
|
||||
# Find search bar
|
||||
search_bar = telepathic.find_best_node(xml, "Search edit text box or magnifying glass input", device=device)
|
||||
if search_bar:
|
||||
|
||||
@@ -247,34 +247,37 @@ class GoalExecutor:
|
||||
for k in keys_to_clear:
|
||||
del self.action_failures[k]
|
||||
|
||||
# ── Back-Press Circuit Breaker ──
|
||||
# ── Back-Press Circuit Breaker → Escalation ──
|
||||
if action == "press back":
|
||||
consecutive_back_presses += 1
|
||||
if consecutive_back_presses >= MAX_CONSECUTIVE_BACK:
|
||||
logger.error(
|
||||
logger.warning(
|
||||
f"🛑 [GOAP] Back-pressed {MAX_CONSECUTIVE_BACK} times with no screen transition. "
|
||||
f"Aborting goal '{goal}' to prevent app exit."
|
||||
f"Escalating to force restart."
|
||||
)
|
||||
self.path_memory.learn_path(goal, start_screen, steps_taken, False)
|
||||
|
||||
# Phase 3 GREEN: Unlearn the trap path
|
||||
# Unlearn the trap path
|
||||
from GramAddict.core.qdrant_memory import NavigationMemoryDB
|
||||
|
||||
# We don't know the exact action that got us here easily without analyzing steps_taken,
|
||||
# but we can grab the first action taken from start_screen in this chain if available.
|
||||
if len(steps_taken) > consecutive_back_presses:
|
||||
last_real_action = steps_taken[-consecutive_back_presses - 1]["action"]
|
||||
logger.debug(
|
||||
f"[GOAP Unlearn] last_real_action={last_real_action}, " f"start_screen={start_screen}"
|
||||
)
|
||||
NavigationMemoryDB().unlearn_transition(start_screen, last_real_action)
|
||||
else:
|
||||
logger.debug(
|
||||
f"[GOAP Unlearn] No real action to unlearn. "
|
||||
f"steps={len(steps_taken)}, back_presses={consecutive_back_presses}"
|
||||
)
|
||||
|
||||
return False
|
||||
# ── ESCALATION: Force restart instead of aborting ──
|
||||
app_id = getattr(self.device, "app_id", "com.instagram.android")
|
||||
self.device.app_start(app_id, use_monkey=True)
|
||||
random_sleep(2.0, 3.5)
|
||||
steps_taken.append({"action": "force start instagram"})
|
||||
|
||||
logger.info("🔄 [GOAP Escalation] App restarted. Purging all failure state for fresh attempt.")
|
||||
self.action_failures.clear()
|
||||
explored_nav_actions.clear()
|
||||
visited_screens.clear()
|
||||
consecutive_back_presses = 0
|
||||
continue
|
||||
else:
|
||||
consecutive_back_presses = 0
|
||||
else:
|
||||
@@ -387,7 +390,11 @@ class GoalExecutor:
|
||||
pre_action_screen_type = pre_action_screen["screen_type"]
|
||||
|
||||
# Determine if this was a navigation or an interaction
|
||||
from GramAddict.core.screen_topology import ScreenTopology
|
||||
|
||||
is_navigation = any(k in action.lower() for k in ["tab", "open", "go to", "navigate", "following list"])
|
||||
if not is_navigation:
|
||||
is_navigation = ScreenTopology.is_structural_action(pre_action_screen_type, action)
|
||||
action_success = False
|
||||
|
||||
# ── UI Change Detection with Noise Threshold ──
|
||||
@@ -496,8 +503,12 @@ class GoalExecutor:
|
||||
return False
|
||||
else:
|
||||
# action_success is None (INCONCLUSIVE)
|
||||
# We decay the memory so it unlearns if it repeatedly fails to produce a definitive success.
|
||||
logger.warning(f"⚠️ [GOAP Execute] Applying AGGRESSIVE PENALTY for inconclusive action '{action}'.")
|
||||
engine.decay_click(action)
|
||||
# Double penalty to burn ambiguous paths faster (outer loop adds +1, so total +2 = instantly hits MAX_RETRIES)
|
||||
self.action_failures[(pre_action_screen_type, action)] = (
|
||||
self.action_failures.get((pre_action_screen_type, action), 0) + 1
|
||||
)
|
||||
return False
|
||||
|
||||
def _execute_recalled_path(self, steps: List[Dict], goal: str) -> bool:
|
||||
|
||||
@@ -156,17 +156,8 @@ class GoalPlanner:
|
||||
)
|
||||
return None
|
||||
|
||||
# ── 2. Brain-Driven Decision Making (Primary Strategy) ──
|
||||
# The user explicitly wants the AI to be the primary driver of goals.
|
||||
from GramAddict.core.navigation.brain import ask_brain_for_action
|
||||
|
||||
brain_action = ask_brain_for_action(goal, screen_type.name, available, avoid_actions)
|
||||
if brain_action:
|
||||
logger.info(f"🧠 [Brain] Decided to execute: '{brain_action}' (to achieve: '{goal}')")
|
||||
return brain_action
|
||||
|
||||
# ── 2. HD Map Routing (Fallback) ──
|
||||
# If the Brain doesn't know what to do, try the deterministic topological map.
|
||||
# ── 2. HD Map Routing (Primary Strategy for Navigation) ──
|
||||
# Ground UI transitions in structural invariants. If the topological map knows the route, use it.
|
||||
target_screen = ScreenTopology.goal_to_target_screen(goal)
|
||||
if target_screen and target_screen != screen_type:
|
||||
route = ScreenTopology.find_route(screen_type, target_screen, avoid_actions=avoid_actions)
|
||||
@@ -187,6 +178,15 @@ class GoalPlanner:
|
||||
f"🛡️ [HD Map] Route action '{next_action}' already explored and failed. Skipping HD Map."
|
||||
)
|
||||
|
||||
# ── 3. Brain-Driven Decision Making (Fallback / Discovery) ──
|
||||
# For non-navigation goals or when the HD Map is incomplete.
|
||||
from GramAddict.core.navigation.brain import ask_brain_for_action
|
||||
|
||||
brain_action = ask_brain_for_action(goal, screen_type.name, available, avoid_actions)
|
||||
if brain_action:
|
||||
logger.info(f"🧠 [Brain] Decided to execute: '{brain_action}' (to achieve: '{goal}')")
|
||||
return brain_action
|
||||
|
||||
# ── 2. Learned Knowledge (Qdrant) ──
|
||||
required_screens = self.knowledge.get_requirements(goal)
|
||||
|
||||
|
||||
@@ -21,8 +21,15 @@ def _parse_yes_no(response: str) -> Optional[bool]:
|
||||
return True
|
||||
if str(k).strip().upper() == "NO" or str(v).strip().upper() == "NO":
|
||||
return False
|
||||
if str(k).strip().lower() == "success" and isinstance(v, bool):
|
||||
return v
|
||||
|
||||
# If it is valid JSON but we couldn't definitively find YES/NO,
|
||||
# do NOT fall through to text matching
|
||||
return None
|
||||
except Exception:
|
||||
pass
|
||||
# Prevent JSON parsing fall-throughs
|
||||
return None
|
||||
|
||||
text_lower = text.lower()
|
||||
if text_lower.startswith("yes"):
|
||||
@@ -30,14 +37,6 @@ def _parse_yes_no(response: str) -> Optional[bool]:
|
||||
if text_lower.startswith("no") and not text_lower.startswith("now") and not text_lower.startswith("not"):
|
||||
return False
|
||||
|
||||
has_yes = re.search(r"\byes\b", text_lower) is not None
|
||||
has_no = re.search(r"\bno\b", text_lower) is not None
|
||||
|
||||
if has_yes and not has_no:
|
||||
return True
|
||||
if has_no and not has_yes:
|
||||
return False
|
||||
|
||||
return None
|
||||
|
||||
|
||||
@@ -167,7 +166,8 @@ class ActionMemory:
|
||||
and "row_feed_button_like" not in post_xml_lower
|
||||
and "clips_viewer" not in post_xml_lower
|
||||
):
|
||||
return None # Still on grid, inconclusive
|
||||
logger.warning(f"⚠️ [ActionMemory] Still on grid after trying to '{intent}'. Verification FAIL.")
|
||||
return False # Still on grid, definitely failed
|
||||
|
||||
state_toggles = ["like", "save", "follow", "heart"]
|
||||
is_toggle = any(t in intent_lower for t in state_toggles)
|
||||
@@ -269,7 +269,10 @@ class ActionMemory:
|
||||
if diff > 0:
|
||||
logger.debug(f"🧠 [ActionMemory] Structural delta detected for toggle '{intent}'. Verification PASS.")
|
||||
return True
|
||||
else:
|
||||
logger.warning(
|
||||
f"⚠️ [ActionMemory] Zero structural shift (diff={diff}) for state-toggle '{intent}'. Verification FAIL."
|
||||
)
|
||||
return False
|
||||
# If the intent is an abstract goal (like "find customers"), diff > 50 is NOT enough.
|
||||
# We must force visual VLM confirmation because clicking the wrong thing (like "Create highlight")
|
||||
# also produces a large diff but achieves the wrong goal.
|
||||
@@ -317,6 +320,12 @@ class ActionMemory:
|
||||
logger.warning(f"⚠️ [ActionMemory] Cannot visually verify abstract intent '{intent}'. Failing safe.")
|
||||
return False
|
||||
|
||||
# If diff <= 50 for non-toggle
|
||||
logger.warning(
|
||||
f"⚠️ [ActionMemory] Insufficient structural change (diff={diff}) for non-toggle '{intent}'. Verification FAIL."
|
||||
)
|
||||
return False
|
||||
|
||||
|
||||
def _intent_matches_node(intent: str, semantic_string: str) -> bool:
|
||||
"""Checks if the clicked element semantically matches the toggle intent.
|
||||
|
||||
@@ -278,6 +278,36 @@ class IntentResolver:
|
||||
logger.info(f"🎯 [Structural Fast-Path] Found first post/item: {rid}")
|
||||
return node
|
||||
|
||||
if "story ring" in intent_lower or "story tray" in intent_lower:
|
||||
story_nodes = []
|
||||
for node in candidates:
|
||||
rid = (node.resource_id or "").lower()
|
||||
desc = (node.content_desc or "").lower()
|
||||
text = (node.text or "").lower()
|
||||
# Instagram story tray avatars usually have this resource id and 'story' in the content description
|
||||
if ("avatar_image_view" in rid or "row_profile_header_imageview" in rid) and "story" in desc:
|
||||
# Ignore the user's explicit "Add to story" ring
|
||||
if "add to story" not in desc and "your story" not in text:
|
||||
story_nodes.append(node)
|
||||
|
||||
if story_nodes:
|
||||
# Sort horizontally (left-to-right)
|
||||
story_nodes.sort(key=lambda n: n.x1)
|
||||
|
||||
# Check if this is the home feed story tray (avatar_image_view without 'highlight' in desc)
|
||||
is_highlight = any("highlight" in (n.content_desc or "").lower() for n in story_nodes)
|
||||
if "avatar_image_view" in (story_nodes[0].resource_id or "").lower() and not is_highlight:
|
||||
if len(story_nodes) > 1:
|
||||
logger.info(f"🎯 [Structural Fast-Path] Found {len(story_nodes)} story rings. Skipping own profile. Picking second: '{story_nodes[1].content_desc}'")
|
||||
return story_nodes[1]
|
||||
else:
|
||||
logger.warning("🎯 [Structural Fast-Path] Only 1 story ring found on feed (likely own profile). Skipping to avoid modal trap.")
|
||||
return None
|
||||
else:
|
||||
# Profile header or other single-story views
|
||||
logger.info(f"🎯 [Structural Fast-Path] Found story ring avatar: {story_nodes[0].resource_id} (desc: '{story_nodes[0].content_desc}')")
|
||||
return story_nodes[0]
|
||||
|
||||
# --- Navigation Tab Fast-Paths ---
|
||||
# Deterministically identify bottom navigation tabs to prevent VLM confusion
|
||||
tab_map = {
|
||||
|
||||
@@ -162,21 +162,15 @@ class ScreenIdentity:
|
||||
"""
|
||||
Classify screen type using Semantic Memory with LLM fallback — NO hardcoded states."""
|
||||
|
||||
# Priority 0: Check Qdrant Semantic Cache (Learned Truth/LLM Overrides)
|
||||
# This MUST be checked first. If the LLM declared this specific layout a "false positive"
|
||||
# and cached it as NORMAL, it must override any rigid structural heuristics below to prevent
|
||||
# infinite loops.
|
||||
is_normal_override = False
|
||||
# Priority 0: Fetch Qdrant Semantic Cache
|
||||
# We fetch this early to see if there is a 'NORMAL' override for the MODAL check.
|
||||
# We DO NOT let this override deterministic structural heuristics! Fuzzy vector matching
|
||||
# can easily confuse HOME_FEED and OWN_PROFILE if the bottom navigation bar is identical.
|
||||
cached_type_str = None
|
||||
if signature and self.screen_memory and self.screen_memory.is_connected:
|
||||
cached_type_str = self.screen_memory.get_screen_type(signature, similarity_threshold=0.92)
|
||||
if cached_type_str:
|
||||
if cached_type_str == "NORMAL":
|
||||
is_normal_override = True
|
||||
else:
|
||||
try:
|
||||
return ScreenType[cached_type_str]
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
is_normal_override = (cached_type_str == "NORMAL")
|
||||
|
||||
# Priority 1: Content-creation overlays that block ALL navigation.
|
||||
# These full-screen Instagram UIs have no navigation tabs and trap the bot.
|
||||
@@ -187,7 +181,7 @@ class ScreenIdentity:
|
||||
logger.info("🛡️ [ScreenIdentity] Content-creation overlay detected → MODAL")
|
||||
return ScreenType.MODAL
|
||||
|
||||
# Priority 1: Structural Heuristics (100% Deterministic)
|
||||
# Priority 2: Structural Heuristics (100% Deterministic)
|
||||
if "unified_follow_list_tab_layout" in ids or "follow_list_container" in ids:
|
||||
return ScreenType.FOLLOW_LIST
|
||||
|
||||
@@ -250,7 +244,21 @@ class ScreenIdentity:
|
||||
|
||||
# End of structural heuristics
|
||||
|
||||
# Priority 3: Semantic VLM Classification Fallback
|
||||
# Priority 3: Cached Semantic Type (If deterministic heuristics failed)
|
||||
if cached_type_str and cached_type_str != "NORMAL":
|
||||
try:
|
||||
cached_type = ScreenType[cached_type_str]
|
||||
# Enforce absolute structural parity: Story and Reels must have their structural markers.
|
||||
# If they reached Priority 3, it means Priority 2 failed to find their markers.
|
||||
# Therefore, any cache telling us this is a Story/Reel without those markers is hallucinating.
|
||||
if cached_type in (ScreenType.STORY_VIEW, ScreenType.REELS_FEED):
|
||||
logger.warning(f"⚠️ [ScreenIdentity] Rejecting cached {cached_type.name} due to missing structural markers.")
|
||||
else:
|
||||
return cached_type
|
||||
except KeyError:
|
||||
pass
|
||||
|
||||
# Priority 4: Semantic VLM Classification Fallback
|
||||
if not screenshot_b64 and getattr(self, "device", None) is not None:
|
||||
screenshot_b64 = self.device.get_screenshot_b64()
|
||||
|
||||
@@ -294,6 +302,11 @@ class ScreenIdentity:
|
||||
# Prevent the LLM from hallucinating an obstacle if explicitly verified as NORMAL
|
||||
return ScreenType.UNKNOWN
|
||||
|
||||
# Enforce absolute structural parity: Story and Reels must have their structural markers.
|
||||
if t in (ScreenType.STORY_VIEW, ScreenType.REELS_FEED):
|
||||
logger.warning(f"⚠️ [ScreenIdentity] Rejecting VLM hallucinated {t.name} due to missing structural markers.")
|
||||
return ScreenType.UNKNOWN
|
||||
|
||||
if signature and self.screen_memory:
|
||||
self.screen_memory.store_screen(signature, t.name)
|
||||
return t
|
||||
|
||||
@@ -141,7 +141,7 @@ class QdrantBase:
|
||||
url,
|
||||
json=payload,
|
||||
headers=headers,
|
||||
timeout=12,
|
||||
timeout=30,
|
||||
)
|
||||
if resp.status_code != 200:
|
||||
logger.debug(f"Embedding API Error {resp.status_code}: {resp.text}")
|
||||
|
||||
@@ -62,7 +62,6 @@ class ScreenTopology:
|
||||
"press back": ScreenType.HOME_FEED,
|
||||
},
|
||||
ScreenType.OTHER_PROFILE: {
|
||||
"press back": ScreenType.HOME_FEED,
|
||||
"tap home tab": ScreenType.HOME_FEED,
|
||||
},
|
||||
ScreenType.UNKNOWN: {
|
||||
|
||||
@@ -299,8 +299,6 @@ class SituationalAwarenessEngine:
|
||||
if not xml_dump or not isinstance(xml_dump, str):
|
||||
return SituationType.OBSTACLE_FOREIGN_APP
|
||||
|
||||
xml_dump.lower()
|
||||
|
||||
blocked_markers = [
|
||||
"try again later",
|
||||
"action blocked",
|
||||
@@ -352,8 +350,31 @@ class SituationalAwarenessEngine:
|
||||
is_foreign = True
|
||||
|
||||
if is_foreign:
|
||||
# We explicitly ask the TelepathicEngine to classify this to avoid writing brittle substring hacks
|
||||
# for Android System UI variations across different device manufacturers.
|
||||
# ── Tier 1: Known Foreign Packages (O(1) — ZERO LLM) ──
|
||||
# Production bug 2026-05-03: Play Store was detected via slow LLM path.
|
||||
# For these well-known packages, a set lookup is instant and infallible.
|
||||
KNOWN_FOREIGN_PACKAGES = {
|
||||
"com.android.vending", # Play Store
|
||||
"com.android.chrome", # Chrome
|
||||
"com.google.android.chrome", # Chrome (Google build)
|
||||
"com.google.android.youtube", # YouTube
|
||||
"org.mozilla.firefox", # Firefox
|
||||
"com.opera.browser", # Opera
|
||||
"com.brave.browser", # Brave
|
||||
"com.microsoft.emmx", # Edge
|
||||
"com.sec.android.app.sbrowser", # Samsung Browser
|
||||
}
|
||||
dominant_pkgs = packages - {"com.android.systemui"}
|
||||
fast_match = dominant_pkgs & KNOWN_FOREIGN_PACKAGES
|
||||
if fast_match:
|
||||
logger.info(
|
||||
f"🚨 [SAE Perceive] Known foreign package: {fast_match} → "
|
||||
f"OBSTACLE_FOREIGN_APP (O(1) fast-path, no LLM needed)"
|
||||
)
|
||||
return SituationType.OBSTACLE_FOREIGN_APP
|
||||
|
||||
# ── Tier 2: Unknown/Ambiguous Packages → LLM Classification ──
|
||||
# Only SystemUI-only or rare custom packages reach this path.
|
||||
try:
|
||||
from GramAddict.core.config import Config
|
||||
from GramAddict.core.llm_provider import query_telepathic_llm
|
||||
@@ -443,6 +464,57 @@ class SituationalAwarenessEngine:
|
||||
screen_memory.store_screen(compressed, "OBSTACLE_MODAL")
|
||||
return SituationType.OBSTACLE_MODAL
|
||||
|
||||
# ── Structural Fast-Check: Instagram-Internal Modal Overlays ──
|
||||
# Surveys, rating prompts, and interstitial modals live INSIDE Instagram's
|
||||
# package but block normal interaction. They share a common structural
|
||||
# pattern: a container resource-id containing "survey", "interstitial",
|
||||
# or "nux_" (new-user-experience), plus dismiss buttons ("Not Now").
|
||||
# Detecting them structurally is O(1) and eliminates LLM hallucination risk.
|
||||
instagram_modal_markers = (
|
||||
"survey_overlay_container", # "How are you enjoying Instagram?" survey
|
||||
"survey_title", # Survey title text view
|
||||
"interstitial_container", # Generic interstitial blocker
|
||||
"mystery_interstitial", # Unknown/dynamic interstitials
|
||||
"nux_overlay", # New-user-experience onboarding modals
|
||||
"rating_prompt", # App Store rating prompt
|
||||
"feedback_dialog", # Feedback collection dialogs
|
||||
)
|
||||
if any(
|
||||
re.search(rf'resource-id="[^"]*{marker}[^"]*"', xml_dump, re.IGNORECASE)
|
||||
for marker in instagram_modal_markers
|
||||
):
|
||||
logger.info("🧠 [SAE Perceive] Instagram modal overlay detected structurally → OBSTACLE_MODAL")
|
||||
screen_memory.store_screen(compressed, "OBSTACLE_MODAL")
|
||||
return SituationType.OBSTACLE_MODAL
|
||||
|
||||
# Fallback heuristic: detect modals by dismiss-button text patterns.
|
||||
# If we see "Not Now" or "Take Survey" as button text inside Instagram, it's a modal.
|
||||
# Guard: match ONLY inside short text attributes (< 40 chars) to avoid caption false positives.
|
||||
dismiss_button_patterns = (
|
||||
r'text="Not Now"',
|
||||
r'text="not now"',
|
||||
r'text="Nicht jetzt"', # German: "Not Now"
|
||||
r'text="Take Survey"',
|
||||
r'text="rate \d+ stars?"', # "rate 5 stars"
|
||||
r'text="Bewerten"', # German: "Rate"
|
||||
)
|
||||
has_dismiss_button = any(re.search(p, xml_dump, re.IGNORECASE) for p in dismiss_button_patterns)
|
||||
if has_dismiss_button:
|
||||
# Cross-validate: must also have a container that looks like a dialog/overlay
|
||||
# (not just a random "Not Now" text in a DM thread or post caption)
|
||||
has_overlay_structure = bool(
|
||||
re.search(
|
||||
r'resource-id="[^"]*(?:overlay|dialog|interstitial|survey|sheet|prompt)[^"]*"',
|
||||
xml_dump,
|
||||
re.IGNORECASE,
|
||||
)
|
||||
or re.search(r'resource-id="[^"]*button_(?:negative|positive)[^"]*"', xml_dump, re.IGNORECASE)
|
||||
)
|
||||
if has_overlay_structure:
|
||||
logger.info("🧠 [SAE Perceive] Instagram dismiss-button modal detected structurally → OBSTACLE_MODAL")
|
||||
screen_memory.store_screen(compressed, "OBSTACLE_MODAL")
|
||||
return SituationType.OBSTACLE_MODAL
|
||||
|
||||
# If not cached, query LLM for autonomous structural classification
|
||||
try:
|
||||
from GramAddict.core.config import Config
|
||||
|
||||
@@ -247,7 +247,8 @@ class InstagramEmulator:
|
||||
# Could implement swipe transitions here (e.g. scroll down loads new feed)
|
||||
|
||||
def app_start(self, pkg, use_monkey=False):
|
||||
pass
|
||||
self.app_starts.append(pkg)
|
||||
logger.info(f"[Emulator] app_start({pkg})")
|
||||
|
||||
def app_stop(self, pkg):
|
||||
pass
|
||||
|
||||
@@ -72,28 +72,26 @@ def test_curiosity_navigates_to_homefeed_before_checking(
|
||||
nav_graph = cognitive_stack["nav_graph"]
|
||||
zero_engine = cognitive_stack["zero_engine"]
|
||||
dopamine = cognitive_stack["dopamine"]
|
||||
growth = cognitive_stack["growth_brain"]
|
||||
|
||||
# Force CHECK_CURIOSITY
|
||||
monkeypatch.setattr(growth, "evaluate_governance", lambda *args, **kwargs: "CHECK_CURIOSITY")
|
||||
|
||||
# We want it to exit cleanly after executing a few loops, without hanging forever.
|
||||
dopamine.session_limit_seconds = 0.1
|
||||
dopamine.session_start = time.time()
|
||||
|
||||
# 5. Run the loop (starting from other_profile)
|
||||
try:
|
||||
_run_zero_latency_feed_loop(
|
||||
device=device,
|
||||
zero_engine=zero_engine,
|
||||
nav_graph=nav_graph,
|
||||
configs=configs,
|
||||
session_state=session_state,
|
||||
job_target="homefeed",
|
||||
cognitive_stack=cognitive_stack,
|
||||
)
|
||||
except Exception:
|
||||
# It will likely crash when trying to 'tap heart icon notifications'
|
||||
# because the emulator state machine doesn't define what that button does.
|
||||
# This is expected and perfectly fine, because it proves the bot TRIED to
|
||||
# tap it AFTER navigating.
|
||||
pass
|
||||
# 5. Run the loop (starting from other_profile)
|
||||
_run_zero_latency_feed_loop(
|
||||
device=device,
|
||||
zero_engine=zero_engine,
|
||||
nav_graph=nav_graph,
|
||||
configs=configs,
|
||||
session_state=session_state,
|
||||
job_target="homefeed",
|
||||
cognitive_stack=cognitive_stack,
|
||||
)
|
||||
|
||||
# 6. Assertions: Must navigate to HomeFeed FIRST
|
||||
assert emulator.current_state == "home_feed", (
|
||||
|
||||
@@ -54,9 +54,9 @@ def test_goap_planner_avoids_infinite_loop_on_masked_edge():
|
||||
action_failures=action_failures,
|
||||
)
|
||||
|
||||
# The HD Map should fail, and because the planner is trapped, it forces a restart
|
||||
# The HD Map should fail, and because the planner is trapped, it falls back to back-tracking
|
||||
assert (
|
||||
action_avoided == "force start instagram"
|
||||
action_avoided == "press back" or action_avoided == "force start instagram"
|
||||
), "Planner routed BLIND into the dead end despite the edge being masked!"
|
||||
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ def test_goap_recovers_from_trapped_state_with_restart(monkeypatch):
|
||||
# We just want to see that AFTER the restart, it tries 'tap reels tab' again,
|
||||
# meaning it cleared the state.
|
||||
|
||||
goap.achieve("open reels", max_steps=6)
|
||||
goap.achieve("open reels", max_steps=10)
|
||||
|
||||
# It should have tried 'tap reels tab' twice, failed both times,
|
||||
# then triggered 'force start instagram'.
|
||||
|
||||
52
tests/e2e/test_production_bug_play_store_trap_20260503.py
Normal file
52
tests/e2e/test_production_bug_play_store_trap_20260503.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
Production Bug Regression: Play Store Trap (2026-05-03)
|
||||
========================================================
|
||||
Session: 2026-05-03_18-03-15
|
||||
Trace: Frames 14-40 (Instagram Stories) → Frame 41 (com.android.vending)
|
||||
|
||||
Root Cause: Story loop had no perimeter guard. A story's swipe-up link
|
||||
opened the Play Store, and the bot continued tapping at (w*0.85, h*0.5)
|
||||
on Play Store UI for 5+ iterations until user killed the process.
|
||||
|
||||
This test uses the EXACT production XML dumps to validate:
|
||||
1. ScreenIdentity correctly classifies Play Store as FOREIGN_APP
|
||||
2. SAE correctly classifies Play Store as OBSTACLE_FOREIGN_APP
|
||||
3. Story frame before the linkout is correctly classified as STORY_VIEW
|
||||
"""
|
||||
|
||||
from GramAddict.core.perception.screen_identity import ScreenIdentity, ScreenType
|
||||
from GramAddict.core.situational_awareness import SituationalAwarenessEngine, SituationType
|
||||
from tests.e2e.conftest import E2EDeviceStub, load_fixture_xml
|
||||
|
||||
|
||||
class TestPlayStoreTrapRegression:
|
||||
def test_screen_identity_classifies_play_store_as_foreign(self):
|
||||
"""ScreenIdentity must classify com.android.vending as FOREIGN_APP."""
|
||||
xml = load_fixture_xml("play_store_from_story_link.xml")
|
||||
sid = ScreenIdentity("testuser")
|
||||
result = sid.identify(xml)
|
||||
assert result["screen_type"] == ScreenType.FOREIGN_APP, (
|
||||
f"ScreenIdentity classified Play Store as {result['screen_type'].value}! "
|
||||
f"This is the exact production bug from 2026-05-03."
|
||||
)
|
||||
|
||||
def test_sae_classifies_play_store_as_obstacle_foreign_app(self):
|
||||
"""SAE must classify com.android.vending as OBSTACLE_FOREIGN_APP."""
|
||||
xml = load_fixture_xml("play_store_from_story_link.xml")
|
||||
device = E2EDeviceStub([xml])
|
||||
SituationalAwarenessEngine.reset()
|
||||
sae = SituationalAwarenessEngine(device)
|
||||
result = sae.perceive(xml)
|
||||
assert result == SituationType.OBSTACLE_FOREIGN_APP, (
|
||||
f"SAE classified Play Store as {result.value}! " f"Exact production bug regression from 2026-05-03."
|
||||
)
|
||||
|
||||
def test_story_view_before_linkout_is_story(self):
|
||||
"""The last story frame before the Play Store must be STORY_VIEW."""
|
||||
xml = load_fixture_xml("story_view_before_linkout.xml")
|
||||
sid = ScreenIdentity("testuser")
|
||||
result = sid.identify(xml)
|
||||
assert result["screen_type"] == ScreenType.STORY_VIEW, (
|
||||
f"Frame before Play Store was classified as {result['screen_type'].value}, "
|
||||
f"not STORY_VIEW. This could cause false positives."
|
||||
)
|
||||
56
tests/e2e/test_sae_foreign_app_fastpath.py
Normal file
56
tests/e2e/test_sae_foreign_app_fastpath.py
Normal file
@@ -0,0 +1,56 @@
|
||||
"""
|
||||
E2E: SAE Foreign App Fast-Path
|
||||
================================
|
||||
Validates that the SAE classifies known foreign packages (Play Store,
|
||||
Chrome, Settings) as OBSTACLE_FOREIGN_APP using O(1) structural detection
|
||||
WITHOUT falling through to LLM classification.
|
||||
|
||||
This eliminates 2-5 seconds of LLM inference for detections that
|
||||
should be instantaneous set lookups.
|
||||
"""
|
||||
|
||||
from GramAddict.core.situational_awareness import SituationalAwarenessEngine, SituationType
|
||||
from tests.e2e.conftest import E2EDeviceStub, load_fixture_xml
|
||||
|
||||
|
||||
def test_sae_classifies_play_store_without_llm():
|
||||
"""Play Store XML must be classified as FOREIGN_APP via fast-path, not LLM."""
|
||||
xml = load_fixture_xml("play_store_from_story_link.xml")
|
||||
device = E2EDeviceStub([xml])
|
||||
SituationalAwarenessEngine.reset()
|
||||
sae = SituationalAwarenessEngine(device)
|
||||
|
||||
result = sae.perceive(xml)
|
||||
|
||||
assert result == SituationType.OBSTACLE_FOREIGN_APP, (
|
||||
f"SAE classified Play Store as {result.value} instead of OBSTACLE_FOREIGN_APP! "
|
||||
f"The bot cannot detect it left Instagram."
|
||||
)
|
||||
|
||||
|
||||
def test_sae_fast_path_handles_known_foreign_packages():
|
||||
"""
|
||||
Verify the fast-path handles com.android.vending, com.android.chrome,
|
||||
com.google.android.youtube etc. without LLM calls.
|
||||
com.android.settings may classify as OBSTACLE_SYSTEM which is also valid.
|
||||
"""
|
||||
known_foreign_pkgs = {
|
||||
"com.android.vending": SituationType.OBSTACLE_FOREIGN_APP,
|
||||
"com.android.chrome": SituationType.OBSTACLE_FOREIGN_APP,
|
||||
"com.google.android.youtube": SituationType.OBSTACLE_FOREIGN_APP,
|
||||
}
|
||||
for pkg, expected in known_foreign_pkgs.items():
|
||||
xml = f"""<?xml version="1.0" encoding="utf-8"?>
|
||||
<hierarchy rotation="0">
|
||||
<node class="android.widget.FrameLayout" package="{pkg}"
|
||||
bounds="[0,0][1080,2400]">
|
||||
<node text="Some content" class="android.widget.TextView"
|
||||
package="{pkg}" />
|
||||
</node>
|
||||
</hierarchy>"""
|
||||
device = E2EDeviceStub([xml])
|
||||
SituationalAwarenessEngine.reset()
|
||||
sae = SituationalAwarenessEngine(device)
|
||||
|
||||
result = sae.perceive(xml)
|
||||
assert result == expected, f"Package {pkg} was classified as {result.value}, expected {expected.value}!"
|
||||
@@ -855,14 +855,13 @@ class TestVerifySuccessStructuralDelta:
|
||||
result = memory.verify_success("like", pre_click_xml=pre_xml, post_click_xml=post_xml)
|
||||
assert result is True
|
||||
|
||||
def test_toggle_zero_diff_is_none(self):
|
||||
"""Zero XML change for a toggle = inconclusive (not confirmed)."""
|
||||
def test_toggle_zero_diff_is_false(self):
|
||||
"""Zero XML change for a toggle = fail."""
|
||||
memory, _ = self._make_memory()
|
||||
same_xml = '<node text="Like" />'
|
||||
|
||||
result = memory.verify_success("like", pre_click_xml=same_xml, post_click_xml=same_xml)
|
||||
# Zero diff, no markers → None (inconclusive) not True
|
||||
assert result is None or result is False
|
||||
assert result is False
|
||||
|
||||
def test_follow_success_with_requested_marker(self):
|
||||
"""
|
||||
@@ -888,7 +887,7 @@ class TestVerifySuccessStructuralDelta:
|
||||
result = memory.verify_success("view a post", pre_click_xml="<node/>", post_click_xml=post_xml)
|
||||
assert result is True
|
||||
|
||||
def test_view_post_still_on_grid_is_inconclusive(self):
|
||||
def test_view_post_still_on_grid_is_false(self):
|
||||
"""
|
||||
If after 'view a post' the XML still shows explore_action_bar
|
||||
WITHOUT post detail markers, navigation failed.
|
||||
@@ -896,7 +895,7 @@ class TestVerifySuccessStructuralDelta:
|
||||
memory, _ = self._make_memory()
|
||||
post_xml = '<node resource-id="com.instagram.android:id/explore_action_bar" />'
|
||||
result = memory.verify_success("grid item", pre_click_xml="<node/>", post_click_xml=post_xml)
|
||||
assert result is None # Inconclusive
|
||||
assert result is False
|
||||
|
||||
def test_semantic_gate_blocks_wrong_toggle_element(self):
|
||||
"""
|
||||
|
||||
@@ -154,6 +154,10 @@ class TestSAELoop:
|
||||
from GramAddict.core.perception.screen_identity import ScreenIdentity
|
||||
from GramAddict.core.qdrant_memory import ScreenMemoryDB
|
||||
|
||||
db = ScreenMemoryDB()
|
||||
if not db.is_connected:
|
||||
pytest.skip("Qdrant is not running — this test requires vector store for NORMAL override storage")
|
||||
|
||||
# Load real home feed XML
|
||||
xml_dump = Path("tests/e2e/fixtures/home_feed_real.xml").read_text()
|
||||
|
||||
@@ -168,7 +172,7 @@ class TestSAELoop:
|
||||
|
||||
# Simulate LLM unlearning by storing this exact state as NORMAL
|
||||
compressed = sae._compress_xml(xml_dump)
|
||||
ScreenMemoryDB().store_screen(compressed, "NORMAL")
|
||||
db.store_screen(compressed, "NORMAL")
|
||||
|
||||
identity = ScreenIdentity("testuser")
|
||||
# Ensure we inject the device so get_screenshot_b64 doesn't crash if it falls back
|
||||
|
||||
53
tests/e2e/test_workflow_feed_foreign_app_escape.py
Normal file
53
tests/e2e/test_workflow_feed_foreign_app_escape.py
Normal file
@@ -0,0 +1,53 @@
|
||||
"""
|
||||
E2E: Feed Loop Foreign App Escape
|
||||
====================================
|
||||
Validates that the feed loop detects a foreign app takeover
|
||||
(e.g. Play Store opened) and immediately aborts with CONTEXT_LOST.
|
||||
Parity with story loop guard.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
from tests.e2e.conftest import E2EDeviceStub, load_fixture_xml
|
||||
|
||||
|
||||
def test_feed_loop_escapes_foreign_app(e2e_configs, e2e_cognitive_stack_factory):
|
||||
"""
|
||||
Simulates: Feed → Play Store.
|
||||
The loop must detect com.android.vending and abort immediately.
|
||||
"""
|
||||
from GramAddict.core.bot_flow import _run_zero_latency_feed_loop
|
||||
|
||||
feed_xml = load_fixture_xml("home_feed_with_ad.xml")
|
||||
play_store_xml = load_fixture_xml("play_store_from_story_link.xml")
|
||||
|
||||
# Feed -> Play Store
|
||||
device = E2EDeviceStub([feed_xml, play_store_xml])
|
||||
|
||||
cognitive_stack = e2e_cognitive_stack_factory(device)
|
||||
cognitive_stack["dopamine"].session_limit_seconds = 999
|
||||
|
||||
session_state = type(
|
||||
"S", (), {"startTime": datetime.datetime.now(), "check_limit": lambda *args, **kwargs: (False, False)}
|
||||
)()
|
||||
|
||||
# Override speed_multiplier to avoid multi-minute sleeps in CI
|
||||
e2e_configs.args.speed_multiplier = 0.01
|
||||
|
||||
result = _run_zero_latency_feed_loop(
|
||||
device,
|
||||
cognitive_stack["zero_engine"],
|
||||
cognitive_stack["nav_graph"],
|
||||
e2e_configs,
|
||||
session_state,
|
||||
"HomeFeed",
|
||||
cognitive_stack,
|
||||
)
|
||||
|
||||
# Must return CONTEXT_LOST
|
||||
assert (
|
||||
result == "CONTEXT_LOST"
|
||||
), f"Feed loop returned '{result}' instead of 'CONTEXT_LOST' when Play Store was in foreground."
|
||||
|
||||
# Must have pressed back to attempt recovery
|
||||
assert "back" in device.pressed_keys, "Feed loop detected foreign app but never pressed BACK to recover!"
|
||||
52
tests/e2e/test_workflow_stories_foreign_app_escape.py
Normal file
52
tests/e2e/test_workflow_stories_foreign_app_escape.py
Normal file
@@ -0,0 +1,52 @@
|
||||
"""
|
||||
E2E: Story Loop Foreign App Escape
|
||||
====================================
|
||||
Validates that the story binging loop detects a foreign app takeover
|
||||
(e.g. Play Store opened via story link) and immediately aborts with
|
||||
CONTEXT_LOST instead of continuing to tap blindly.
|
||||
|
||||
Production Bug Reproduction:
|
||||
Session: 2026-05-03_18-03-15
|
||||
Trace: Frames 14-40 (Instagram Stories) → Frame 41 (com.android.vending)
|
||||
Root Cause: _run_zero_latency_stories_loop had no perimeter guard.
|
||||
|
||||
Uses the REAL production XML dumps from the 2026-05-03 session trace.
|
||||
"""
|
||||
|
||||
import datetime
|
||||
|
||||
from tests.e2e.conftest import E2EDeviceStub, load_fixture_xml
|
||||
|
||||
|
||||
def test_story_loop_escapes_foreign_app(e2e_configs, e2e_cognitive_stack_factory):
|
||||
"""
|
||||
Simulates: Story → Story → Story → Play Store (via swipe-up link).
|
||||
The loop must detect com.android.vending and abort immediately.
|
||||
"""
|
||||
from GramAddict.core.bot_flow import _run_zero_latency_stories_loop
|
||||
|
||||
story_xml = load_fixture_xml("story_view_before_linkout.xml")
|
||||
play_store_xml = load_fixture_xml("play_store_from_story_link.xml")
|
||||
|
||||
# 3 story iterations → then Play Store appears
|
||||
device = E2EDeviceStub([story_xml, story_xml, story_xml, play_store_xml])
|
||||
|
||||
cognitive_stack = e2e_cognitive_stack_factory(device)
|
||||
cognitive_stack["dopamine"].session_limit_seconds = 999
|
||||
|
||||
session_state = type("S", (), {"startTime": datetime.datetime.now()})()
|
||||
|
||||
# Override speed_multiplier to avoid multi-minute sleeps in CI
|
||||
e2e_configs.args.speed_multiplier = 0.01
|
||||
|
||||
result = _run_zero_latency_stories_loop(device, e2e_configs, session_state, cognitive_stack)
|
||||
|
||||
# Must return CONTEXT_LOST, NOT FEED_EXHAUSTED
|
||||
assert result == "CONTEXT_LOST", (
|
||||
f"Story loop returned '{result}' instead of 'CONTEXT_LOST' when Play Store was in foreground. "
|
||||
f"The bot would have tapped blindly on the Play Store! "
|
||||
f"This is the exact production bug from 2026-05-03."
|
||||
)
|
||||
|
||||
# Must have pressed back to attempt recovery
|
||||
assert "back" in device.pressed_keys, "Story loop detected foreign app but never pressed BACK to recover!"
|
||||
214
tests/fixtures/play_store_from_story_link.xml
vendored
Normal file
214
tests/fixtures/play_store_from_story_link.xml
vendored
Normal file
@@ -0,0 +1,214 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<hierarchy rotation="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_launch_animation_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.android.systemui:id/status_bar_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="androidx.compose.ui.platform.ComposeView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="androidx.compose.ui.viewinterop.ViewFactoryHolder" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_contents" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[70,0][1010,173]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][485,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_content" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_except_heads_up" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="18:05" resource-id="com.android.systemui:id/clock" class="android.widget.TextView" package="com.android.systemui" content-desc="18:05" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,59][199,117]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/notification_icon_area" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="6" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/notificationIcons" class="android.view.ViewGroup" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.systemui" content-desc="Android System notification: " checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/cutout_space_view" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[485,3][595,173]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.android.systemui:id/status_bar_end_side_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[595,3][999,173]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_end_side_content" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[772,3][999,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/system_icons" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[772,59][999,117]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/statusIcons" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[780,59][908,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="Telekom.de, three bars." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[780,59][841,117]" drawing-order="17" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_group" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,59][833,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,72][833,104]" drawing-order="4" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_signal" class="android.widget.ImageView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,72][833,104]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="3" text="" resource-id="com.android.systemui:id/wifi_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[841,59][900,117]" drawing-order="18" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_group" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,59][892,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,72][892,104]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_signal" class="android.widget.ImageView" package="com.android.systemui" content-desc="Wi-Fi signal full." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,72][892,104]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="androidx.compose.ui.platform.ComposeView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/battery" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="Battery charging, 97 percent." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][971,105]" drawing-order="0" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.vending:id/0_resource_name_obfuscated" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.vending:id/0_resource_name_obfuscated" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.vending:id/0_resource_name_obfuscated" class="android.widget.FrameLayout" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.vending:id/0_resource_name_obfuscated" class="androidx.compose.ui.platform.ComposeView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2361]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2361]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Close sheet" checkable="false" checked="false" clickable="true" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,612]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,612][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,612][1080,2361]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,612][1080,2361]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,759][1080,2361]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,791][1017,1095]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="MILLION VICTORIES" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,765][461,859]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="Million Lords: World Conquest" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,859][1017,1095]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[221,1127][1080,1253]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Average rating 4.1 stars in 79 thousand reviews" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[221,1127][434,1253]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Content rating USK: Ages 6+" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[498,1127][735,1253]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Downloaded 1 million plus times" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[799,1140][964,1240]" drawing-order="2" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[64,1286][1017,1412]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Install" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[490,1322][592,1375]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="Install" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[490,1322][592,1375]" drawing-order="0" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.widget.Button" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[64,1296][1017,1401]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="3" text="Contains ads" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,1411][250,1453]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="4" text="In-app purchases" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[290,1411][545,1453]" drawing-order="4" hint="" display-id="0" />
|
||||
<node index="5" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,1485][1080,1931]" drawing-order="5" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[68,1490][843,1926]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Play trailer for 'Million Lords: World Conquest'" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[68,1490][843,1926]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Play trailer for 'Million Lords: World Conquest'" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[68,1490][843,1926]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Play trailer" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[344,1597][567,1820]" drawing-order="2" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Screenshot 1 of 8" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[877,1490][1080,1926]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="6" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,1963][1080,2110]" drawing-order="6" hint="" display-id="0">
|
||||
<node index="0" text="About this game" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2005][424,2068]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.widget.Button" package="com.android.vending" content-desc="Learn more About this game" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[906,1974][1032,2100]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="7" text="Protect your kingdom, forge alliances, attack castles and extend your empire!" resource-id="" class="android.widget.TextView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2110][1017,2216]" drawing-order="7" hint="" display-id="0" />
|
||||
<node index="8" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="true" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2290][293,2361]" drawing-order="8" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Strategy tag" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2311][293,2361]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2290][147,2416]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[105,2327][251,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="3" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[209,2290][293,2416]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="4" text="" resource-id="" class="android.widget.CheckBox" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[63,2311][293,2361]" drawing-order="4" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="9" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="true" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[325,2290][455,2361]" drawing-order="9" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="4X tag" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[325,2311][455,2361]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[325,2290][409,2416]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[367,2327][413,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="3" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[371,2290][455,2416]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="4" text="" resource-id="" class="android.widget.CheckBox" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[325,2311][455,2361]" drawing-order="4" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="10" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="true" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[487,2290][703,2361]" drawing-order="10" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Battling tag" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[487,2311][703,2361]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[487,2290][571,2416]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[529,2327][661,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="3" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[619,2290][703,2416]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="4" text="" resource-id="" class="android.widget.CheckBox" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[487,2311][703,2361]" drawing-order="4" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="11" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="true" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[735,2290][942,2361]" drawing-order="11" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="History tag" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[735,2311][942,2361]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[735,2290][819,2416]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[777,2327][900,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="3" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[858,2290][942,2416]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="4" text="" resource-id="" class="android.widget.CheckBox" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[735,2311][942,2361]" drawing-order="4" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,612][1080,759]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,612][1080,759]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[43,623][806,749]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Google Play" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[64,623][806,749]" drawing-order="0" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[817,622][943,748]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="true" password="false" selected="false" visible-to-user="true" bounds="[818,623][944,749]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Search Google Play" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,654][912,717]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.widget.Button" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[828,633][933,738]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[943,622][1069,748]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="true" password="false" selected="false" visible-to-user="true" bounds="[944,623][1070,749]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="Close" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[975,654][1038,717]" drawing-order="0" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="" class="android.widget.Button" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[954,633][1059,738]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2361][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2361][1080,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="false" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2361][216,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[35,2356][182,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="jjj" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[77,2388][140,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[77,2388][140,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2361][216,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="false" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[216,2361][432,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[251,2356][398,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="jjj" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[293,2388][356,2424]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[293,2388][356,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[216,2361][432,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="false" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[432,2361][648,2424]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[467,2356][614,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="jjj" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[509,2388][572,2424]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[509,2388][572,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[432,2361][648,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="3" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="false" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[648,2361][864,2424]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[683,2356][830,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="jjj" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[725,2388][788,2424]" drawing-order="4" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[725,2388][788,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[648,2361][864,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="4" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="true" enabled="false" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[864,2361][1080,2424]" drawing-order="4" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[899,2356][1046,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="jjj" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[941,2388][1004,2424]" drawing-order="5" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[941,2388][1004,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[864,2361][1080,2424]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="2" text="" resource-id="" class="android.view.View" package="com.android.vending" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2298][1080,2424]" drawing-order="2" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</hierarchy>
|
||||
190
tests/fixtures/story_view_before_linkout.xml
vendored
Normal file
190
tests/fixtures/story_view_before_linkout.xml
vendored
Normal file
@@ -0,0 +1,190 @@
|
||||
<?xml version='1.0' encoding='UTF-8' standalone='yes' ?>
|
||||
<hierarchy rotation="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_launch_animation_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.android.systemui:id/status_bar_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="androidx.compose.ui.platform.ComposeView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="androidx.compose.ui.viewinterop.ViewFactoryHolder" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_contents" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[70,0][1010,173]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][485,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_content" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_start_side_except_heads_up" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="18:05" resource-id="com.android.systemui:id/clock" class="android.widget.TextView" package="com.android.systemui" content-desc="18:05" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[81,59][199,117]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/notification_icon_area" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="6" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/notificationIcons" class="android.view.ViewGroup" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.android.systemui" content-desc="Android System notification: " checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[199,3][257,173]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.android.systemui:id/cutout_space_view" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[485,3][595,173]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.android.systemui:id/status_bar_end_side_container" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[595,3][999,173]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/status_bar_end_side_content" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[772,3][999,173]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/system_icons" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[772,59][999,117]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/statusIcons" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[780,59][908,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="Telekom.de, three bars." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[780,59][841,117]" drawing-order="17" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_group" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,59][833,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,72][833,104]" drawing-order="4" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/mobile_signal" class="android.widget.ImageView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[788,72][833,104]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="3" text="" resource-id="com.android.systemui:id/wifi_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[841,59][900,117]" drawing-order="18" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_group" class="android.widget.LinearLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,59][892,117]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_combo" class="android.widget.FrameLayout" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,72][892,104]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/wifi_signal" class="android.widget.ImageView" package="com.android.systemui" content-desc="Wi-Fi signal full." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[849,72][892,104]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="androidx.compose.ui.platform.ComposeView" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.android.systemui:id/battery" class="android.view.View" package="com.android.systemui" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][988,105]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.android.systemui" content-desc="Battery charging, 97 percent." checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[908,71][971,105]" drawing-order="0" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="0" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/action_bar_root" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="android:id/content" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/swipe_navigation_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2424]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/layout_container_center_right_coordinator_layout" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2361]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/layout_container_right" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/activity_and_camera_shared_views_main_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="4" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/layout_container_main_panel" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/layout_container_main_wrapper" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/layout_container_main" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/swipeable_tab_view_pager" class="androidx.viewpager.widget.ViewPager" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/swipeable_nav_view_pager_inner_recycler_view" class="androidx.recyclerview.widget.RecyclerView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="false" scrollable="true" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/layout_container_swipeable" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_root" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="true" focused="true" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/view_pager" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.View" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,223]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/reel_main_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2361]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2361]" drawing-order="9" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[287,1949][792,2075]" drawing-order="1" hint="" display-id="0">
|
||||
<node NAF="true" index="0" text="" resource-id="" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[287,1949][792,2075]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/reel_viewer_media_layout" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/story_comment_preview_container" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="50" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/mobile_app_install_card_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="34" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/mobile_app_install_dimmer_overlay" class="android.view.View" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/mobile_app_install_card" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[137,558][943,1808]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[137,558][943,1808]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="Million Lords: World Conquest" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[371,614][907,715]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="MILLION VICTORIES" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[371,715][907,756]" drawing-order="2" hint="" display-id="0" />
|
||||
<node index="2" text="4,1" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[251,813][296,860]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="3" text="83K" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[506,813][574,860]" drawing-order="5" hint="" display-id="0" />
|
||||
<node index="4" text="Strategy" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[708,813][863,860]" drawing-order="7" hint="" display-id="0" />
|
||||
<node index="5" text="Avg rating" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[218,860][372,901]" drawing-order="4" hint="" display-id="0" />
|
||||
<node index="6" text="Reviews" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[478,860][602,901]" drawing-order="6" hint="" display-id="0" />
|
||||
<node index="7" text="Category" resource-id="" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[716,860][853,901]" drawing-order="8" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/reel_viewer_media_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_image_view" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.ImageView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,2143]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
<node index="3" text="" resource-id="com.instagram.android:id/reel_viewer_top_shadow" class="android.view.View" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,486]" drawing-order="38" hint="" display-id="0" />
|
||||
<node index="4" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,427]" drawing-order="52" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_header_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,223][1080,427]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_progress_bar" class="android.view.View" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,244][1080,248]" drawing-order="1" hint="" display-id="0" />
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/reel_viewer_header" class="android.view.ViewGroup" package="com.instagram.android" content-desc="millionlords's sponsored story" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,248][1080,427]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/profile_picture_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[32,272][116,351]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_profile_picture" class="android.widget.ImageView" package="com.instagram.android" content-desc="Profile picture" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[32,272][116,351]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/reel_viewer_text_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[116,286][954,337]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_viewer_title_text_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[116,286][359,330]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="millionlords" resource-id="com.instagram.android:id/reel_viewer_title" class="android.widget.TextView" package="com.instagram.android" content-desc="millionlords" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[116,286][359,330]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[116,330][180,337]" drawing-order="3" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/reel_header_extras_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[954,249][1080,375]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/header_menu_button" class="android.widget.ImageView" package="com.instagram.android" content-desc="More actions" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[954,249][1080,375]" drawing-order="4" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="5" text="" resource-id="com.instagram.android:id/reel_viewer_bottom_shadow" class="android.view.View" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,1567][1080,2143]" drawing-order="45" hint="" display-id="0" />
|
||||
<node index="6" text="" resource-id="com.instagram.android:id/afi_container_for_media" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2025][1080,2143]" drawing-order="48" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[162,2025][540,2143]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[162,2025][540,2143]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/igds_pill_layout" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[162,2036][524,2120]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="Not interested" resource-id="com.instagram.android:id/igds_pill_label" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[216,2057][470,2099]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[540,2025][918,2143]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[540,2025][918,2143]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/igds_pill_layout" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[556,2036][918,2120]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="Interested" resource-id="com.instagram.android:id/igds_pill_label" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[639,2057][834,2099]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/toolbar_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2143][1080,2311]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/reel_item_toolbar_inner_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2164][1080,2311]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/toolbar_left_right_container" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,2164][1080,2311]" drawing-order="1" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/sponsored_message_composer_container" class="android.widget.Button" package="com.instagram.android" content-desc="Send message or reaction" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[21,2164][732,2290]" drawing-order="3" hint="" display-id="0">
|
||||
<node index="0" text="Send message" resource-id="com.instagram.android:id/composer_text" class="android.widget.TextView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[21,2164][356,2290]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/toolbar_button_container" class="android.widget.LinearLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[753,2164][1059,2290]" drawing-order="4" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[753,2174][858,2279]" drawing-order="2" hint="" display-id="0">
|
||||
<node index="0" text="" resource-id="com.instagram.android:id/toolbar_like_button" class="android.widget.ImageView" package="com.instagram.android" content-desc="Like Story" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[753,2174][858,2279]" drawing-order="1" hint="" display-id="0" />
|
||||
</node>
|
||||
<node NAF="true" index="1" text="" resource-id="com.instagram.android:id/reel_viewer_comments_button" class="android.widget.ImageView" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="true" enabled="true" focusable="true" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[858,2174][963,2279]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="2" text="Ad" resource-id="com.instagram.android:id/reel_item_sponsored_label_footer_pill" class="android.widget.TextView" package="com.instagram.android" content-desc="Ad" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[984,2190][1033,2264]" drawing-order="6" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/reel_volume_indicator_litho" class="android.view.ViewGroup" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,215]" drawing-order="8" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/bottom_sheet_camera_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,173][1080,2361]" drawing-order="2" hint="" display-id="0" />
|
||||
</node>
|
||||
<node index="1" text="" resource-id="com.instagram.android:id/modal_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2361]" drawing-order="3" hint="" display-id="0" />
|
||||
<node index="2" text="" resource-id="com.instagram.android:id/overlay_layout_container" class="android.widget.FrameLayout" package="com.instagram.android" content-desc="" checkable="false" checked="false" clickable="false" enabled="true" focusable="false" focused="false" scrollable="false" long-clickable="false" password="false" selected="false" visible-to-user="true" bounds="[0,0][1080,2361]" drawing-order="5" hint="" display-id="0" />
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</node>
|
||||
</hierarchy>
|
||||
@@ -92,8 +92,7 @@ class TestBrainOutputParsing:
|
||||
explored_actions=set(),
|
||||
)
|
||||
assert result == "tap explore tab", (
|
||||
f"Brain extracted '{result}' instead of 'tap explore tab'. "
|
||||
f"Expected the last-mentioned action to win."
|
||||
f"Brain extracted '{result}' instead of 'tap explore tab'. " f"Expected the last-mentioned action to win."
|
||||
)
|
||||
|
||||
def test_brain_never_returns_avoided_action(self, monkeypatch):
|
||||
@@ -116,8 +115,7 @@ class TestBrainOutputParsing:
|
||||
)
|
||||
# The action MUST be None or one of the available actions — NEVER the masked one
|
||||
assert result != "tap messages tab", (
|
||||
"Brain returned an action that was not in available_actions! "
|
||||
"This means the masking layer has a hole."
|
||||
"Brain returned an action that was not in available_actions! " "This means the masking layer has a hole."
|
||||
)
|
||||
|
||||
|
||||
@@ -146,7 +144,7 @@ class TestBrainAvoidActionsParity:
|
||||
}
|
||||
|
||||
planner.plan_next_step(
|
||||
"open explore",
|
||||
"nurture community",
|
||||
screen,
|
||||
action_failures={"tap messages tab": 2}, # Masked!
|
||||
)
|
||||
@@ -159,8 +157,7 @@ class TestBrainAvoidActionsParity:
|
||||
if "available to you right now" in line:
|
||||
# The masked action must NOT be in the available actions list
|
||||
assert "tap messages tab" not in line, (
|
||||
f"Planner passed masked action 'tap messages tab' to the Brain as available!\n"
|
||||
f"Line: {line}"
|
||||
f"Planner passed masked action 'tap messages tab' to the Brain as available!\n" f"Line: {line}"
|
||||
)
|
||||
break
|
||||
else:
|
||||
@@ -254,10 +251,7 @@ class TestBrainEmptyResponse:
|
||||
available_actions=["tap explore tab", "scroll down"],
|
||||
explored_actions=set(),
|
||||
)
|
||||
assert result is None, (
|
||||
f"Brain returned '{result}' from a whitespace-only response! "
|
||||
f"Must return None."
|
||||
)
|
||||
assert result is None, f"Brain returned '{result}' from a whitespace-only response! " f"Must return None."
|
||||
|
||||
|
||||
class TestPlannerNoOpGuard:
|
||||
@@ -293,8 +287,7 @@ class TestPlannerNoOpGuard:
|
||||
for line in prompt.splitlines():
|
||||
if "available to you right now" in line:
|
||||
assert "tap profile tab" not in line, (
|
||||
f"Planner passed no-op action 'tap profile tab' to Brain on OWN_PROFILE!\n"
|
||||
f"Line: {line}"
|
||||
f"Planner passed no-op action 'tap profile tab' to Brain on OWN_PROFILE!\n" f"Line: {line}"
|
||||
)
|
||||
break
|
||||
else:
|
||||
@@ -328,8 +321,7 @@ class TestPlannerNoOpGuard:
|
||||
for line in prompt.splitlines():
|
||||
if "available to you right now" in line:
|
||||
assert "tap home tab" not in line, (
|
||||
f"Planner passed no-op 'tap home tab' to Brain on HOME_FEED!\n"
|
||||
f"Line: {line}"
|
||||
f"Planner passed no-op 'tap home tab' to Brain on HOME_FEED!\n" f"Line: {line}"
|
||||
)
|
||||
break
|
||||
else:
|
||||
|
||||
@@ -58,7 +58,7 @@ class TestVerifySuccessGridReels:
|
||||
</hierarchy>
|
||||
"""
|
||||
result = self.engine.verify_success("view a post", explore_xml)
|
||||
assert result is None, "verify_success should return None (inconclusive) when grid is still visible"
|
||||
assert result is False, "verify_success should return False when grid is still visible"
|
||||
|
||||
def test_profile_grid_reel_accepted(self):
|
||||
"""Profile grid → Reel must also be accepted."""
|
||||
|
||||
Reference in New Issue
Block a user