@@ -1310,6 +1310,93 @@ def _sanitize_avd_name(device_name: str) -> str:
13101310 return sanitized
13111311
13121312
1313+ def _configure_avd_hardware (avd_name : str ) -> bool :
1314+ """
1315+ Configure AVD hardware settings to ensure buttons work properly.
1316+ Sets hw.keyboard=yes in config.ini so hardware buttons are functional.
1317+
1318+ Args:
1319+ avd_name: Name of the AVD
1320+
1321+ Returns:
1322+ True if successful, False otherwise
1323+ """
1324+ try :
1325+ # Find AVD config directory
1326+ # AVDs are stored in ~/.android/avd/{avd_name}.avd/ on Linux/macOS
1327+ # or %USERPROFILE%\.android\avd\{avd_name}.avd\ on Windows
1328+ system = platform .system ()
1329+
1330+ if system == "Windows" :
1331+ avd_home = os .environ .get ('ANDROID_AVD_HOME' )
1332+ if not avd_home :
1333+ user_profile = os .environ .get ('USERPROFILE' , os .environ .get ('HOME' , '' ))
1334+ avd_home = os .path .join (user_profile , '.android' , 'avd' )
1335+ avd_home = os .path .expandvars (avd_home )
1336+ else :
1337+ # Linux/macOS
1338+ avd_home = os .environ .get ('ANDROID_AVD_HOME' )
1339+ if not avd_home :
1340+ avd_home = os .path .join (os .path .expanduser ('~' ), '.android' , 'avd' )
1341+
1342+ config_path = Path (avd_home ) / f"{ avd_name } .avd" / "config.ini"
1343+
1344+ if not config_path .exists ():
1345+ print (f"[installer][emulator] AVD config file not found: { config_path } " )
1346+ return False
1347+
1348+ # Read current config
1349+ with open (config_path , 'r' , encoding = 'utf-8' ) as f :
1350+ lines = f .readlines ()
1351+
1352+ # Modify or add hw.keyboard setting
1353+ # hw.keyboard=yes enables hardware input so buttons work
1354+ modified = False
1355+ new_lines = []
1356+ hw_keyboard_found = False
1357+
1358+ for line in lines :
1359+ stripped = line .strip ()
1360+ if stripped .startswith ('hw.keyboard=' ):
1361+ # Replace existing setting
1362+ new_lines .append ('hw.keyboard=yes\n ' )
1363+ hw_keyboard_found = True
1364+ if 'no' in stripped .lower ():
1365+ modified = True
1366+ else :
1367+ new_lines .append (line )
1368+
1369+ # Add setting if not found
1370+ if not hw_keyboard_found :
1371+ # Add after other hw.* settings if any, otherwise at the end
1372+ insert_pos = len (new_lines )
1373+ for i , line in enumerate (new_lines ):
1374+ if line .strip ().startswith ('hw.' ) and i < len (new_lines ) - 1 :
1375+ # Check if next line doesn't start with hw.
1376+ if i + 1 < len (new_lines ) and not new_lines [i + 1 ].strip ().startswith ('hw.' ):
1377+ insert_pos = i + 1
1378+ break
1379+
1380+ new_lines .insert (insert_pos , 'hw.keyboard=yes\n ' )
1381+ modified = True
1382+
1383+ # Write back if modified
1384+ if modified :
1385+ with open (config_path , 'w' , encoding = 'utf-8' ) as f :
1386+ f .writelines (new_lines )
1387+ print (f"[installer][emulator] Configured hardware settings (hw.keyboard=yes) for AVD '{ avd_name } '" )
1388+ return True
1389+ else :
1390+ print (f"[installer][emulator] Hardware settings already configured for AVD '{ avd_name } '" )
1391+ return True
1392+
1393+ except Exception as e :
1394+ print (f"[installer][emulator] Failed to configure hardware settings for AVD '{ avd_name } ': { e } " )
1395+ import traceback
1396+ traceback .print_exc ()
1397+ return False
1398+
1399+
13131400def _get_highest_api_system_image (system_images : list [dict ]) -> str | None :
13141401 """
13151402 Get the system image with the highest API level.
@@ -1637,6 +1724,9 @@ async def create_avd_from_system_image(device_param: str) -> bool:
16371724
16381725 print (f"[installer][emulator] AVD '{ avd_name } ' created successfully" )
16391726
1727+ # Configure hardware settings so buttons work properly
1728+ _configure_avd_hardware (avd_name )
1729+
16401730 # Note: AVD list will be automatically refreshed when services_list is requested
16411731 # in long_poll_handler.py, so no manual refresh is needed here
16421732
0 commit comments