File size: 5,181 Bytes
399b80c
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
import platform
from openspace.utils.logging import Logger
from typing import Dict, Any, Optional

logger = Logger.get_logger(__name__)

platform_name = platform.system()


class AccessibilityHelper:
    def __init__(self):
        self.platform = platform_name
        self.adapter = None
        
        try:
            if platform_name == "Darwin":
                from ..platform_adapters.macos_adapter import MacOSAdapter
                self.adapter = MacOSAdapter()
            elif platform_name == "Linux":
                from ..platform_adapters.linux_adapter import LinuxAdapter
                self.adapter = LinuxAdapter()
            elif platform_name == "Windows":
                from ..platform_adapters.windows_adapter import WindowsAdapter
                self.adapter = WindowsAdapter()
        except ImportError as e:
            logger.warning(f"Failed to import platform adapter: {e}")
    
    def get_tree(self, max_depth: int = 10) -> Dict[str, Any]:
        if not self.adapter:
            return {
                'error': f'No adapter available for {self.platform}',
                'platform': self.platform
            }
        
        try:
            return self.adapter.get_accessibility_tree(max_depth=max_depth)
        except Exception as e:
            logger.error(f"Failed to get accessibility tree: {e}")
            return {
                'error': str(e),
                'platform': self.platform
            }
    
    def is_available(self) -> bool:
        return self.adapter is not None and hasattr(self.adapter, 'available') and self.adapter.available
    
    def find_element_by_name(self, tree: Dict[str, Any], name: str) -> Optional[Dict[str, Any]]:
        if not tree or 'tree' not in tree:
            return None
        
        return self._search_tree(tree['tree'], 'name', name)
    
    def find_element_by_role(self, tree: Dict[str, Any], role: str) -> Optional[Dict[str, Any]]:
        if not tree or 'tree' not in tree:
            return None
        
        return self._search_tree(tree['tree'], 'role', role)
    
    def _search_tree(self, node: Dict[str, Any], key: str, value: str) -> Optional[Dict[str, Any]]:
        if not node:
            return None
        
        # Check current node
        if key in node and node[key] == value:
            return node
        
        # Recursively search child nodes
        if 'children' in node:
            for child in node['children']:
                result = self._search_tree(child, key, value)
                if result:
                    return result
        
        return None
    
    def flatten_tree(self, tree: Dict[str, Any]) -> list:
        if not tree or 'tree' not in tree:
            return []
        
        result = []
        self._flatten_node(tree['tree'], result)
        return result
    
    def _flatten_node(self, node: Dict[str, Any], result: list):
        """Recursively flatten nodes"""
        if not node:
            return
        
        # Add current node (remove children)
        node_copy = {k: v for k, v in node.items() if k != 'children'}
        result.append(node_copy)
        
        # Recursively process child nodes
        if 'children' in node:
            for child in node['children']:
                self._flatten_node(child, result)
    
    def get_visible_elements(self, tree: Dict[str, Any]) -> list:
        all_elements = self.flatten_tree(tree)
        
        visible = []
        for element in all_elements:
            if self.platform == "Linux":
                if 'states' in element and 'showing' in element.get('states', []):
                    visible.append(element)
            elif self.platform == "Darwin":
                if element.get('enabled', False):
                    visible.append(element)
            elif self.platform == "Windows":
                if element.get('states', {}).get('is_visible', False):
                    visible.append(element)
        
        return visible
    
    def get_clickable_elements(self, tree: Dict[str, Any]) -> list:
        all_elements = self.flatten_tree(tree)
        
        clickable_roles = [
            'button', 'push-button', 'toggle-button', 'radio-button',
            'link', 'menu-item', 'AXButton', 'AXLink', 'AXMenuItem'
        ]
        
        clickable = []
        for element in all_elements:
            role = element.get('role', '').lower()
            if any(cr in role for cr in clickable_roles):
                clickable.append(element)
        
        return clickable
    
    def get_statistics(self, tree: Dict[str, Any]) -> Dict[str, Any]:
        all_elements = self.flatten_tree(tree)
        
        # Count roles
        roles = {}
        for element in all_elements:
            role = element.get('role', 'unknown')
            roles[role] = roles.get(role, 0) + 1
        
        return {
            'total_elements': len(all_elements),
            'visible_elements': len(self.get_visible_elements(tree)),
            'clickable_elements': len(self.get_clickable_elements(tree)),
            'roles': roles,
            'platform': self.platform
        }