Skip to main content
Glama
hao-cyber
by hao-cyber
system.py13.5 kB
""" System-related functions for Phone MCP. This module provides functions to access system-level information on the phone. """ import asyncio import json import re from ..core import run_command async def get_current_window(): """Get information about the current active window on the device. Retrieves details about the currently focused window, active application, and foreground activities on the device using multiple methods for reliability. Returns: str: JSON string with current window details or error message """ # Check for connected device from ..core import check_device_connection connection_status = await check_device_connection() if "ready" not in connection_status: return connection_status try: window_info = {} success_count = 0 # Method 1: Get current focus information (most reliable) cmd = "adb shell dumpsys window windows" success, output = await run_command(cmd) if success and output.strip(): success_count += 1 # Process the output with Python instead of grep focus_patterns = ["mCurrentFocus", "mFocusedApp"] for line in output.strip().split("\n"): line = line.strip() if any(pattern in line for pattern in focus_patterns): if "mCurrentFocus" in line: window_info["current_focus"] = line.replace( "mCurrentFocus=", "" ).strip() # Extract package and activity match = re.search(r"(\S+)/(\S+)", line) if match: window_info["package_name"] = match.group(1) window_info["activity_name"] = match.group(2) elif "mFocusedApp" in line: window_info["focused_app"] = line.replace( "mFocusedApp=", "" ).strip() # Method 2: Get current resumed activity cmd = "adb shell dumpsys activity activities" success, output = await run_command(cmd) if success and output.strip(): # Use Python to find resumed activities activity_patterns = ["ResumedActivity", "mResumedActivity"] for line in output.strip().split("\n"): if any(pattern in line for pattern in activity_patterns): window_info["resumed_activity"] = line.strip() success_count += 1 break # Method 3: Get top activity using am command cmd = "adb shell am stack list" success, output = await run_command(cmd) if not success or "Error" in output: # Newer Android versions use different command cmd = "adb shell cmd activity activities" success, output = await run_command(cmd) if success and output.strip(): # Python processing for activity/topResumedActivity filtered_lines = [] for line in output.strip().split("\n"): if "ACTIVITY" in line or "topResumedActivity" in line: filtered_lines.append(line.strip()) if filtered_lines: output = "\n".join(filtered_lines) if success and output.strip(): success_count += 1 window_info["activity_stack"] = output.strip().split("\n")[0:2] # Try to extract the package name if we don't have it yet if "package_name" not in window_info: package_match = re.search(r"([a-zA-Z0-9_.]+)/[a-zA-Z0-9_.]+", output) if package_match: window_info["package_name"] = package_match.group(1) # Method 4: Get recent apps info cmd = "adb shell dumpsys activity recents" success, output = await run_command(cmd) if success and output.strip(): # Use Python to find Recent #0 line for line in output.strip().split("\n"): if "Recent #0" in line: window_info["recent_activity"] = line.strip() success_count += 1 break # If we still don't have package info, try direct method if "package_name" not in window_info: cmd = "adb shell dumpsys window" success, output = await run_command(cmd) if success and output.strip(): # Python processing for mCurrentFocus for line in output.strip().split("\n"): if "mCurrentFocus" in line: match = re.search(r"([a-zA-Z0-9_.]+)/[a-zA-Z0-9_.]+", line) if match: window_info["package_name"] = match.group(1) break # Add device screen state cmd = "adb shell dumpsys power" success, output = await run_command(cmd) if success and output.strip(): # Python processing for power state screen_state_patterns = ["mWakefulness=", "Display Power"] for line in output.strip().split("\n"): if any(pattern in line for pattern in screen_state_patterns): window_info["screen_state"] = ( "on" if "ON" in line or "AWAKE" in line else "off" ) break if success_count > 0: return json.dumps(window_info, indent=2) else: # Last resort - just get basic device info cmd = "adb shell getprop ro.product.model" success, model = await run_command(cmd) if success: return json.dumps( { "basic_info": "Unable to get window details", "device_model": model.strip(), }, indent=2, ) else: return "Failed to retrieve current window information" except Exception as e: return f"Error retrieving window information: {str(e)}" async def launch_app_activity(package_component=None, action=None, extra_args=None): """Launch an app by starting a specific activity with custom action and component. This function starts an application by launching a specific activity component. It effectively launches the app and can be used in conjunction with get_app_shortcuts to discover available activities, but does not require it as a prerequisite. Args: package_component (str): App component in format "package/activity" (e.g. "com.example.app/.MainActivity") This is the primary way to specify which app and activity to launch action (str, optional): Intent action to use (e.g. "android.intent.action.VIEW") extra_args (str, optional): Additional intent arguments to pass (e.g. "-d 'content://contacts/people/'") Returns: str: Success message with the launched component name, or error details if launch failed Example: To launch the main activity of an app: launch_app_activity("com.example.app/.MainActivity") To launch a specific activity with an action and data: launch_app_activity("com.android.browser/.BrowserActivity", action="android.intent.action.VIEW", extra_args="-d 'https://example.com'") """ # Check for connected device from ..core import check_device_connection connection_status = await check_device_connection() if "ready" not in connection_status: return connection_status try: cmd_parts = ["adb shell am start"] # Add action if provided if action: cmd_parts.append(f'-a "{action}"') # Add component if provided if package_component: cmd_parts.append(f'-n "{package_component}"') # Add any extra arguments if extra_args: cmd_parts.append(extra_args) # Combine into final command cmd = " ".join(cmd_parts) # Execute the command success, output = await run_command(cmd) if success and ("Starting" in output or "Activity" in output): component = package_component or "specified activity" return f"Successfully launched {component}" else: return f"Failed to launch activity: {output}" except Exception as e: return f"Error launching activity: {str(e)}" async def get_app_shortcuts(package_name=None): """Get application shortcuts for installed apps. Retrieves shortcuts (quick actions) available for Android apps. If package_name is provided, returns shortcuts only for that app, otherwise lists all apps with shortcuts. Args: package_name (str, optional): Specific app package to get shortcuts for Returns: str: JSON string with app shortcuts information or error message """ # Check for connected device from ..core import check_device_connection connection_status = await check_device_connection() if "ready" not in connection_status: return connection_status try: # Use direct dumpsys shortcut command - most reliable method cmd = "adb shell dumpsys shortcut" success, output = await run_command(cmd) if not success or not output.strip(): return "Failed to retrieve shortcut information" # Filter by package name if provided (using Python instead of grep) if package_name: package_found = False filtered_lines = [] line_count = 0 max_lines = 50 # Equivalent to grep -A 50 for line in output.strip().split("\n"): if line.strip().startswith(f"Package: {package_name}"): package_found = True filtered_lines.append(line) line_count = 0 elif package_found and line_count < max_lines: filtered_lines.append(line) line_count += 1 if filtered_lines: output = "\n".join(filtered_lines) else: return f"No shortcuts found for package: {package_name}" # Process the results result = {} # Extract packages with shortcuts packages = [] current_package = None shortcuts = [] lines = output.strip().split("\n") for i, line in enumerate(lines): line = line.strip() if line.startswith("Package:"): # Save previous package data if exists if current_package and shortcuts: result[current_package] = {"shortcuts": shortcuts} # Start new package parts = line.split(":", 1) if len(parts) > 1: current_package = ( parts[1].strip().split()[0] ) # Get package name without UID packages.append(current_package) shortcuts = [] # Extract shortcut info when inside a package section elif current_package and "ShortcutInfo" in line: shortcut = {} shortcut_id_match = re.search(r"\{id=([^,]+)", line) if shortcut_id_match: shortcut["id"] = shortcut_id_match.group(1) # Continue collecting data for this shortcut - use current index instead of searching collect_for = 5 # Collect next 5 lines for this shortcut for j in range(i + 1, min(i + collect_for, len(lines))): subline = lines[j].strip() # Extract shortcut label if "shortLabel=" in subline: label_match = re.search(r"shortLabel=([^,]+)", subline) if label_match: shortcut["label"] = label_match.group(1).strip() # Extract intent data elif "intents=" in subline: intent_match = re.search(r"act=([^ ]+)", subline) if intent_match: shortcut["action"] = intent_match.group(1) component_match = re.search(r"cmp=([^ \}/]+)", subline) if component_match: shortcut["component"] = component_match.group(1) if shortcut.get("id"): shortcuts.append(shortcut) # Add the last package if current_package and shortcuts: result[current_package] = {"shortcuts": shortcuts} # If we're looking for a specific package but didn't find any shortcuts if package_name and package_name not in result: return f"No shortcuts found for package: {package_name}" # If not looking for a specific package, include list of all packages if not package_name: result["all_packages"] = packages return json.dumps(result, indent=2) except Exception as e: return f"Error retrieving app shortcuts: {str(e)}"

Latest Blog Posts

MCP directory API

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

curl -X GET 'https://glama.ai/api/mcp/v1/servers/hao-cyber/phone-mcp'

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