@@ -1405,6 +1405,93 @@ def _sanitize_avd_name(device_name: str) -> str:
14051405 return sanitized
14061406
14071407
1408+ def _configure_avd_hardware (avd_name : str ) -> bool :
1409+ """
1410+ Configure AVD hardware settings to ensure buttons work properly.
1411+ Sets hw.keyboard=yes in config.ini so hardware buttons are functional.
1412+
1413+ Args:
1414+ avd_name: Name of the AVD
1415+
1416+ Returns:
1417+ True if successful, False otherwise
1418+ """
1419+ try :
1420+ # Find AVD config directory
1421+ # AVDs are stored in ~/.android/avd/{avd_name}.avd/ on Linux/macOS
1422+ # or %USERPROFILE%\.android\avd\{avd_name}.avd\ on Windows
1423+ system = platform .system ()
1424+
1425+ if system == "Windows" :
1426+ avd_home = os .environ .get ('ANDROID_AVD_HOME' )
1427+ if not avd_home :
1428+ user_profile = os .environ .get ('USERPROFILE' , os .environ .get ('HOME' , '' ))
1429+ avd_home = os .path .join (user_profile , '.android' , 'avd' )
1430+ avd_home = os .path .expandvars (avd_home )
1431+ else :
1432+ # Linux/macOS
1433+ avd_home = os .environ .get ('ANDROID_AVD_HOME' )
1434+ if not avd_home :
1435+ avd_home = os .path .join (os .path .expanduser ('~' ), '.android' , 'avd' )
1436+
1437+ config_path = Path (avd_home ) / f"{ avd_name } .avd" / "config.ini"
1438+
1439+ if not config_path .exists ():
1440+ print (f"[installer][emulator] AVD config file not found: { config_path } " )
1441+ return False
1442+
1443+ # Read current config
1444+ with open (config_path , 'r' , encoding = 'utf-8' ) as f :
1445+ lines = f .readlines ()
1446+
1447+ # Modify or add hw.keyboard setting
1448+ # hw.keyboard=yes enables hardware input so buttons work
1449+ modified = False
1450+ new_lines = []
1451+ hw_keyboard_found = False
1452+
1453+ for line in lines :
1454+ stripped = line .strip ()
1455+ if stripped .startswith ('hw.keyboard=' ):
1456+ # Replace existing setting
1457+ new_lines .append ('hw.keyboard=yes\n ' )
1458+ hw_keyboard_found = True
1459+ if 'no' in stripped .lower ():
1460+ modified = True
1461+ else :
1462+ new_lines .append (line )
1463+
1464+ # Add setting if not found
1465+ if not hw_keyboard_found :
1466+ # Add after other hw.* settings if any, otherwise at the end
1467+ insert_pos = len (new_lines )
1468+ for i , line in enumerate (new_lines ):
1469+ if line .strip ().startswith ('hw.' ) and i < len (new_lines ) - 1 :
1470+ # Check if next line doesn't start with hw.
1471+ if i + 1 < len (new_lines ) and not new_lines [i + 1 ].strip ().startswith ('hw.' ):
1472+ insert_pos = i + 1
1473+ break
1474+
1475+ new_lines .insert (insert_pos , 'hw.keyboard=yes\n ' )
1476+ modified = True
1477+
1478+ # Write back if modified
1479+ if modified :
1480+ with open (config_path , 'w' , encoding = 'utf-8' ) as f :
1481+ f .writelines (new_lines )
1482+ print (f"[installer][emulator] Configured hardware settings (hw.keyboard=yes) for AVD '{ avd_name } '" )
1483+ return True
1484+ else :
1485+ print (f"[installer][emulator] Hardware settings already configured for AVD '{ avd_name } '" )
1486+ return True
1487+
1488+ except Exception as e :
1489+ print (f"[installer][emulator] Failed to configure hardware settings for AVD '{ avd_name } ': { e } " )
1490+ import traceback
1491+ traceback .print_exc ()
1492+ return False
1493+
1494+
14081495def _get_highest_api_system_image (system_images : list [dict ]) -> str | None :
14091496 """
14101497 Get the system image with the highest API level.
@@ -1732,6 +1819,9 @@ async def create_avd_from_system_image(device_param: str) -> bool:
17321819
17331820 print (f"[installer][emulator] AVD '{ avd_name } ' created successfully" )
17341821
1822+ # Configure hardware settings so buttons work properly
1823+ _configure_avd_hardware (avd_name )
1824+
17351825 # Note: AVD list will be automatically refreshed when services_list is requested
17361826 # in long_poll_handler.py, so no manual refresh is needed here
17371827
0 commit comments